0
0
Fork 1
mirror of https://mau.dev/maunium/synapse.git synced 2024-06-27 15:08:18 +02:00

Merge branch 'develop' of github.com:matrix-org/synapse into registration-api-changes

This commit is contained in:
Kegan Dougal 2014-09-15 09:46:33 +01:00
commit bf6fa6dd3d
73 changed files with 8192 additions and 3952 deletions

View file

@ -1,3 +1,26 @@
Changes in synapse 0.2.3 (2014-09-12)
=====================================
Homeserver:
* Fix bug where we stopped sending events to remote home servers if a
user from that home server left, even if there were some still in the
room.
* Fix bugs in the state conflict resolution where it was incorrectly
rejecting events.
Webclient:
* Display room names and topics.
* Allow setting/editing of room names and topics.
* Display information about rooms on the main page.
* Handle ban and kick events in real time.
* VoIP UI and reliability improvements.
* Add glare support for VoIP.
* Improvements to initial startup speed.
* Don't display duplicate join events.
* Local echo of messages.
* Differentiate sending and sent of local echo.
* Various minor bug fixes.
Changes in synapse 0.2.2 (2014-09-06) Changes in synapse 0.2.2 (2014-09-06)
===================================== =====================================

View file

@ -1 +1 @@
0.2.2 0.2.3

View file

@ -5,3 +5,5 @@ Broad-sweeping stuff which would be nice to have
- homeserver implementation in go - homeserver implementation in go
- homeserver implementation in node.js - homeserver implementation in node.js
- client SDKs - client SDKs
- libpurple library
- irssi plugin?

View file

@ -24,7 +24,7 @@ If you already have an account, you must **login** into it.
`Try out the fiddle`__ `Try out the fiddle`__
.. __: http://jsfiddle.net/4q2jyxng/ .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/register_login
Registration Registration
------------ ------------
@ -87,7 +87,7 @@ user and **send a message** to that room.
`Try out the fiddle`__ `Try out the fiddle`__
.. __: http://jsfiddle.net/zL3zto9g/ .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/create_room_send_msg
Creating a room Creating a room
--------------- ---------------
@ -137,7 +137,7 @@ join a room **via a room alias** if one was set up.
`Try out the fiddle`__ `Try out the fiddle`__
.. __: http://jsfiddle.net/7fhotf1b/ .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/room_memberships
Inviting a user to a room Inviting a user to a room
------------------------- -------------------------
@ -183,7 +183,7 @@ of getting events, depending on what the client already knows.
`Try out the fiddle`__ `Try out the fiddle`__
.. __: http://jsfiddle.net/vw11mg37/ .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/event_stream
Getting all state Getting all state
----------------- -----------------
@ -633,4 +633,4 @@ application.
`Try out the fiddle`__ `Try out the fiddle`__
.. __: http://jsfiddle.net/uztL3yme/ .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/example_app

79
docs/human-id-rules.rst Normal file
View file

@ -0,0 +1,79 @@
This document outlines the format for human-readable IDs within matrix.
Overview
--------
UTF-8 is quickly becoming the standard character encoding set on the web. As
such, Matrix requires that all strings MUST be encoded as UTF-8. However,
using Unicode as the character set for human-readable IDs is troublesome. There
are many different characters which appear identical to each other, but would
identify different users. In addition, there are non-printable characters which
cannot be rendered by the end-user. This opens up a security vulnerability with
phishing/spoofing of IDs, commonly known as a homograph attack.
Web browers encountered this problem when International Domain Names were
introduced. A variety of checks were put in place in order to protect users. If
an address failed the check, the raw punycode would be displayed to disambiguate
the address. Similar checks are performed by home servers in Matrix. However,
Matrix does not use punycode representations, and so does not show raw punycode
on a failed check. Instead, home servers must outright reject these misleading
IDs.
Types of human-readable IDs
---------------------------
There are two main human-readable IDs in question:
- Room aliases
- User IDs
Room aliases look like ``#localpart:domain``. These aliases point to opaque
non human-readable room IDs. These pointers can change, so there is already an
issue present with the same ID pointing to a different destination at a later
date.
User IDs look like ``@localpart:domain``. These represent actual end-users, and
unlike room aliases, there is no layer of indirection. This presents a much
greater concern with homograph attacks.
Checks
------
- Similar to web browsers.
- blacklisted chars (e.g. non-printable characters)
- mix of language sets from 'preferred' language not allowed.
- Language sets from CLDR dataset.
- Treated in segments (localpart, domain)
- Additional restrictions for ease of processing IDs.
- Room alias localparts MUST NOT have ``#`` or ``:``.
- User ID localparts MUST NOT have ``@`` or ``:``.
Rejecting
---------
- Home servers MUST reject room aliases which do not pass the check, both on
GETs and PUTs.
- Home servers MUST reject user ID localparts which do not pass the check, both
on creation and on events.
- Any home server whose domain does not pass this check, MUST use their punycode
domain name instead of the IDN, to prevent other home servers rejecting you.
- Error code is ``M_FAILED_HUMAN_ID_CHECK``. (generic enough for both failing
due to homograph attacks, and failing due to including ``:`` s, etc)
- Error message MAY go into further information about which characters were
rejected and why.
- Error message SHOULD contain a ``failed_keys`` key which contains an array
of strings which represent the keys which failed the check e.g::
failed_keys: [ user_id, room_alias ]
Other considerations
--------------------
- Basic security: Informational key on the event attached by HS to say "unsafe
ID". Problem: clients can just ignore it, and since it will appear only very
rarely, easy to forget when implementing clients.
- Moderate security: Requires client handshake. Forces clients to implement
a check, else they cannot communicate with the misleading ID. However, this is
extra overhead in both client implementations and round-trips.
- High security: Outright rejection of the ID at the point of creation /
receiving event. Point of creation rejection is preferable to avoid the ID
entering the system in the first place. However, malicious HSes can just allow
the ID. Hence, other home servers must reject them if they see them in events.
Client never sees the problem ID, provided the HS is correctly implemented.
- High security decided; client doesn't need to worry about it, no additional
protocol complexity aside from rejection of an event.

View file

@ -1182,16 +1182,16 @@ This event is sent by the caller when they wish to establish a call.
- ``type`` : "string" - The type of session description, in this case 'offer' - ``type`` : "string" - The type of session description, in this case 'offer'
- ``sdp`` : "string" - The SDP text of the session description - ``sdp`` : "string" - The SDP text of the session description
``m.call.candidate`` ``m.call.candidates``
This event is sent by callers after sending an invite and by the callee after answering. This event is sent by callers after sending an invite and by the callee after answering.
Its purpose is to give the other party an additional ICE candidate to try using to Its purpose is to give the other party additional ICE candidates to try using to
communicate. communicate.
Required keys: Required keys:
- ``call_id`` : "string" - The ID of the call this event relates to - ``call_id`` : "string" - The ID of the call this event relates to
- ``version`` : "integer" - The version of the VoIP specification this messages - ``version`` : "integer" - The version of the VoIP specification this messages
adheres to. his specification is version 0. adheres to. his specification is version 0.
- ``candidate`` : "candidate object" - Object describing the candidate. - ``candidates`` : "array of candidate objects" - Array of object describing the candidates.
``Candidate Object`` ``Candidate Object``
@ -1249,7 +1249,33 @@ Or a rejected call:
<------- m.call.hangup <------- m.call.hangup
Calls are negotiated according to the WebRTC specification. Calls are negotiated according to the WebRTC specification.
Glare
-----
This specification aims to address the problem of two users calling each other
at roughly the same time and their invites crossing on the wire. It is a far
better experience for the users if their calls are connected if it is clear
that their intention is to set up a call with one another.
In Matrix, calls are to rooms rather than users (even if those rooms may only
contain one other user) so we consider calls which are to the same room.
The rules for dealing with such a situation are as follows:
- If an invite to a room is received whilst the client is preparing to send an
invite to the same room, the client should cancel its outgoing call and
instead automatically accept the incoming call on behalf of the user.
- If an invite to a room is received after the client has sent an invite to the
same room and is waiting for a response, the client should perform a
lexicographical comparison of the call IDs of the two calls and use the
lesser of the two calls, aborting the greater. If the incoming call is the
lesser, the client should accept this call on behalf of the user.
The call setup should appear seamless to the user as if they had simply placed
a call and the other party had accepted. Thusly, any media stream that had been
setup for use on a call should be transferred and used for the call that
replaces it.
Profiles Profiles
======== ========

View file

@ -19,7 +19,12 @@ $('.login').live('click', function() {
showLoggedIn(data); showLoggedIn(data);
}, },
error: function(err) { error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText))); var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
} }
}); });
}); });

View file

@ -58,7 +58,12 @@ $('.login').live('click', function() {
showLoggedIn(data); showLoggedIn(data);
}, },
error: function(err) { error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText))); var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
} }
}); });
}); });

View file

@ -0,0 +1,7 @@
name: Example Matrix Client
description: Includes login, live event streaming, creating rooms, sending messages and viewing member lists.
authors:
- matrix.org
resources:
- http://matrix.org
normalize_css: no

View file

@ -20,7 +20,12 @@ $('.register').live('click', function() {
showLoggedIn(data); showLoggedIn(data);
}, },
error: function(err) { error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText))); var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
} }
}); });
}); });
@ -36,7 +41,12 @@ var login = function(user, password) {
showLoggedIn(data); showLoggedIn(data);
}, },
error: function(err) { error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText))); var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
} }
}); });
}; };

View file

@ -28,7 +28,12 @@ $('.login').live('click', function() {
showLoggedIn(data); showLoggedIn(data);
}, },
error: function(err) { error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText))); var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
} }
}); });
}); });

View file

@ -16,4 +16,4 @@
""" This is a reference implementation of a synapse home server. """ This is a reference implementation of a synapse home server.
""" """
__version__ = "0.2.2" __version__ = "0.2.3"

View file

@ -69,6 +69,7 @@ class Pdu(JsonEncodedObject):
"prev_state_id", "prev_state_id",
"prev_state_origin", "prev_state_origin",
"required_power_level", "required_power_level",
"user_id",
] ]
internal_keys = [ internal_keys = [

View file

@ -593,6 +593,12 @@ class RoomListHandler(BaseHandler):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_public_room_list(self): def get_public_room_list(self):
chunk = yield self.store.get_rooms(is_public=True) chunk = yield self.store.get_rooms(is_public=True)
for room in chunk:
joined_members = yield self.store.get_room_members(
room_id=room["room_id"],
membership=Membership.JOIN
)
room["num_joined_members"] = len(joined_members)
# FIXME (erikj): START is no longer a valid value # FIXME (erikj): START is no longer a valid value
defer.returnValue({"start": "START", "end": "END", "chunk": chunk}) defer.returnValue({"start": "START", "end": "END", "chunk": chunk})

View file

@ -115,6 +115,8 @@ class StateHandler(object):
is_new = yield self._handle_new_state(new_pdu) is_new = yield self._handle_new_state(new_pdu)
logger.debug("is_new: %s %s %s", is_new, new_pdu.pdu_id, new_pdu.origin)
if is_new: if is_new:
yield self.store.update_current_state( yield self.store.update_current_state(
pdu_id=new_pdu.pdu_id, pdu_id=new_pdu.pdu_id,
@ -134,7 +136,9 @@ class StateHandler(object):
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function
def _handle_new_state(self, new_pdu): def _handle_new_state(self, new_pdu):
tree = yield self.store.get_unresolved_state_tree(new_pdu) tree, missing_branch = yield self.store.get_unresolved_state_tree(
new_pdu
)
new_branch, current_branch = tree new_branch, current_branch = tree
logger.debug( logger.debug(
@ -142,64 +146,17 @@ class StateHandler(object):
new_branch, current_branch new_branch, current_branch
) )
if not current_branch: if missing_branch is not None:
# There is no current state # We're missing some PDUs. Fetch them.
defer.returnValue(True) # TODO (erikj): Limit this.
return missing_prev = tree[missing_branch][-1]
n = new_branch[-1]
c = current_branch[-1]
if n.pdu_id == c.pdu_id and n.origin == c.origin:
# We have all the PDUs we need, so we can just do the conflict
# resolution.
if len(current_branch) == 1:
# This is a direct clobber so we can just...
defer.returnValue(True)
conflict_res = [
self._do_power_level_conflict_res,
self._do_chain_length_conflict_res,
self._do_hash_conflict_res,
]
for algo in conflict_res:
new_res, curr_res = algo(new_branch, current_branch)
if new_res < curr_res:
defer.returnValue(False)
elif new_res > curr_res:
defer.returnValue(True)
raise Exception("Conflict resolution failed.")
else:
# We need to ask for PDUs.
missing_prev = max(
new_branch[-1], current_branch[-1],
key=lambda x: x.depth
)
if not hasattr(missing_prev, "prev_state_id"):
# FIXME Hmm
# temporary fallback
for algo in conflict_res:
new_res, curr_res = algo(new_branch, current_branch)
if new_res < curr_res:
defer.returnValue(False)
elif new_res > curr_res:
defer.returnValue(True)
return
pdu_id = missing_prev.prev_state_id pdu_id = missing_prev.prev_state_id
origin = missing_prev.prev_state_origin origin = missing_prev.prev_state_origin
is_missing = yield self.store.get_pdu(pdu_id, origin) is None is_missing = yield self.store.get_pdu(pdu_id, origin) is None
if not is_missing: if not is_missing:
raise Exception("Conflict resolution failed.") raise Exception("Conflict resolution failed")
yield self._replication.get_pdu( yield self._replication.get_pdu(
destination=missing_prev.origin, destination=missing_prev.origin,
@ -211,23 +168,93 @@ class StateHandler(object):
updated_current = yield self._handle_new_state(new_pdu) updated_current = yield self._handle_new_state(new_pdu)
defer.returnValue(updated_current) defer.returnValue(updated_current)
def _do_power_level_conflict_res(self, new_branch, current_branch): if not current_branch:
max_power_new = max( # There is no current state
new_branch[:-1], defer.returnValue(True)
key=lambda t: t.power_level return
).power_level
max_power_current = max( n = new_branch[-1]
current_branch[:-1], c = current_branch[-1]
key=lambda t: t.power_level
).power_level
return (max_power_new, max_power_current) common_ancestor = n.pdu_id == c.pdu_id and n.origin == c.origin
def _do_chain_length_conflict_res(self, new_branch, current_branch): if common_ancestor:
# We found a common ancestor!
if len(current_branch) == 1:
# This is a direct clobber so we can just...
defer.returnValue(True)
else:
# We didn't find a common ancestor. This is probably fine.
pass
result = yield self._do_conflict_res(
new_branch, current_branch, common_ancestor
)
defer.returnValue(result)
@defer.inlineCallbacks
def _do_conflict_res(self, new_branch, current_branch, common_ancestor):
conflict_res = [
self._do_power_level_conflict_res,
self._do_chain_length_conflict_res,
self._do_hash_conflict_res,
]
for algo in conflict_res:
new_res, curr_res = yield defer.maybeDeferred(
algo,
new_branch, current_branch, common_ancestor
)
if new_res < curr_res:
defer.returnValue(False)
elif new_res > curr_res:
defer.returnValue(True)
raise Exception("Conflict resolution failed.")
@defer.inlineCallbacks
def _do_power_level_conflict_res(self, new_branch, current_branch,
common_ancestor):
new_powers_deferreds = []
for e in new_branch[:-1] if common_ancestor else new_branch:
if hasattr(e, "user_id"):
new_powers_deferreds.append(
self.store.get_power_level(e.context, e.user_id)
)
current_powers_deferreds = []
for e in current_branch[:-1] if common_ancestor else current_branch:
if hasattr(e, "user_id"):
current_powers_deferreds.append(
self.store.get_power_level(e.context, e.user_id)
)
new_powers = yield defer.gatherResults(
new_powers_deferreds,
consumeErrors=True
)
current_powers = yield defer.gatherResults(
current_powers_deferreds,
consumeErrors=True
)
max_power_new = max(new_powers)
max_power_current = max(current_powers)
defer.returnValue(
(max_power_new, max_power_current)
)
def _do_chain_length_conflict_res(self, new_branch, current_branch,
common_ancestor):
return (len(new_branch), len(current_branch)) return (len(new_branch), len(current_branch))
def _do_hash_conflict_res(self, new_branch, current_branch): def _do_hash_conflict_res(self, new_branch, current_branch,
common_ancestor):
new_str = "".join([p.pdu_id + p.origin for p in new_branch]) new_str = "".join([p.pdu_id + p.origin for p in new_branch])
c_str = "".join([p.pdu_id + p.origin for p in current_branch]) c_str = "".join([p.pdu_id + p.origin for p in current_branch])

View file

@ -36,7 +36,7 @@ from .registration import RegistrationStore
from .room import RoomStore from .room import RoomStore
from .roommember import RoomMemberStore from .roommember import RoomMemberStore
from .stream import StreamStore from .stream import StreamStore
from .pdu import StatePduStore, PduStore from .pdu import StatePduStore, PduStore, PdusTable
from .transactions import TransactionStore from .transactions import TransactionStore
from .keys import KeyStore from .keys import KeyStore
@ -47,6 +47,11 @@ import os
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class _RollbackButIsFineException(Exception):
""" This exception is used to rollback a transaction without implying
something went wrong.
"""
pass
class DataStore(RoomMemberStore, RoomStore, class DataStore(RoomMemberStore, RoomStore,
RegistrationStore, StreamStore, ProfileStore, FeedbackStore, RegistrationStore, StreamStore, ProfileStore, FeedbackStore,
@ -71,14 +76,16 @@ class DataStore(RoomMemberStore, RoomStore,
self.min_token -= 1 self.min_token -= 1
stream_ordering = self.min_token stream_ordering = self.min_token
latest = yield self._db_pool.runInteraction( try:
self._persist_pdu_event_txn, yield self._db_pool.runInteraction(
pdu=pdu, self._persist_pdu_event_txn,
event=event, pdu=pdu,
backfilled=backfilled, event=event,
stream_ordering=stream_ordering, backfilled=backfilled,
) stream_ordering=stream_ordering,
defer.returnValue(latest) )
except _RollbackButIsFineException as e:
pass
@defer.inlineCallbacks @defer.inlineCallbacks
def get_event(self, event_id, allow_none=False): def get_event(self, event_id, allow_none=False):
@ -116,6 +123,12 @@ class DataStore(RoomMemberStore, RoomStore,
del cols["content"] del cols["content"]
del cols["prev_pdus"] del cols["prev_pdus"]
cols["content_json"] = json.dumps(pdu.content) cols["content_json"] = json.dumps(pdu.content)
unrec_keys.update({
k: v for k, v in cols.items()
if k not in PdusTable.fields
})
cols["unrecognized_keys"] = json.dumps(unrec_keys) cols["unrecognized_keys"] = json.dumps(unrec_keys)
logger.debug("Persisting: %s", repr(cols)) logger.debug("Persisting: %s", repr(cols))
@ -175,11 +188,12 @@ class DataStore(RoomMemberStore, RoomStore,
try: try:
self._simple_insert_txn(txn, "events", vals) self._simple_insert_txn(txn, "events", vals)
except: except:
logger.exception( logger.warn(
"Failed to persist, probably duplicate: %s", "Failed to persist, probably duplicate: %s",
event.event_id event.event_id,
exc_info=True,
) )
return raise _RollbackButIsFineException("_persist_event")
if not backfilled and hasattr(event, "state_key"): if not backfilled and hasattr(event, "state_key"):
vals = { vals = {
@ -205,8 +219,6 @@ class DataStore(RoomMemberStore, RoomStore,
} }
) )
return self._get_room_events_max_id_txn(txn)
@defer.inlineCallbacks @defer.inlineCallbacks
def get_current_state(self, room_id, event_type=None, state_key=""): def get_current_state(self, room_id, event_type=None, state_key=""):
sql = ( sql = (

View file

@ -17,6 +17,7 @@ import logging
from twisted.internet import defer from twisted.internet import defer
from synapse.api.errors import StoreError from synapse.api.errors import StoreError
from synapse.util.logutils import log_function
import collections import collections
import copy import copy
@ -91,6 +92,7 @@ class SQLBaseStore(object):
self._simple_insert_txn, table, values, or_replace=or_replace self._simple_insert_txn, table, values, or_replace=or_replace
) )
@log_function
def _simple_insert_txn(self, txn, table, values, or_replace=False): def _simple_insert_txn(self, txn, table, values, or_replace=False):
sql = "%s INTO %s (%s) VALUES(%s)" % ( sql = "%s INTO %s (%s) VALUES(%s)" % (
("INSERT OR REPLACE" if or_replace else "INSERT"), ("INSERT OR REPLACE" if or_replace else "INSERT"),
@ -98,6 +100,12 @@ class SQLBaseStore(object):
", ".join(k for k in values), ", ".join(k for k in values),
", ".join("?" for k in values) ", ".join("?" for k in values)
) )
logger.debug(
"[SQL] %s Args=%s Func=%s",
sql, values.values(),
)
txn.execute(sql, values.values()) txn.execute(sql, values.values())
return txn.lastrowid return txn.lastrowid

View file

@ -17,6 +17,7 @@ from twisted.internet import defer
from ._base import SQLBaseStore, Table, JoinHelper from ._base import SQLBaseStore, Table, JoinHelper
from synapse.federation.units import Pdu
from synapse.util.logutils import log_function from synapse.util.logutils import log_function
from collections import namedtuple from collections import namedtuple
@ -308,8 +309,8 @@ class PduStore(SQLBaseStore):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_oldest_pdus_in_context(self, context): def get_oldest_pdus_in_context(self, context):
"""Get a list of Pdus that we haven't backfilled beyond yet (and haven't """Get a list of Pdus that we haven't backfilled beyond yet (and havent
seen). This list is used when we want to backfill backwards and is the seen). This list is used when we want to backfill backwards and is the
list we send to the remote server. list we send to the remote server.
Args: Args:
@ -516,7 +517,7 @@ class StatePduStore(SQLBaseStore):
if not current: if not current:
logger.debug("get_unresolved_state_tree No current state.") logger.debug("get_unresolved_state_tree No current state.")
return return_value return (return_value, None)
return_value.current_branch.append(current) return_value.current_branch.append(current)
@ -524,13 +525,16 @@ class StatePduStore(SQLBaseStore):
txn, new_pdu, current txn, new_pdu, current
) )
missing_branch = None
for branch, prev_state, state in enum_branches: for branch, prev_state, state in enum_branches:
if state: if state:
return_value[branch].append(state) return_value[branch].append(state)
else: else:
# We don't have prev_state :(
missing_branch = branch
break break
return return_value return (return_value, missing_branch)
def update_current_state(self, pdu_id, origin, context, pdu_type, def update_current_state(self, pdu_id, origin, context, pdu_type,
state_key): state_key):
@ -622,53 +626,6 @@ class StatePduStore(SQLBaseStore):
return result return result
def get_next_missing_pdu(self, new_pdu):
"""When we get a new state pdu we need to check whether we need to do
any conflict resolution, if we do then we need to check if we need
to go back and request some more state pdus that we haven't seen yet.
Args:
txn
new_pdu
Returns:
PduIdTuple: A pdu that we are missing, or None if we have all the
pdus required to do the conflict resolution.
"""
return self._db_pool.runInteraction(
self._get_next_missing_pdu, new_pdu
)
def _get_next_missing_pdu(self, txn, new_pdu):
logger.debug(
"get_next_missing_pdu %s %s",
new_pdu.pdu_id, new_pdu.origin
)
current = self._get_current_interaction(
txn,
new_pdu.context, new_pdu.pdu_type, new_pdu.state_key
)
if (not current or not current.prev_state_id
or not current.prev_state_origin):
return None
# Oh look, it's a straight clobber, so wooooo almost no-op.
if (new_pdu.prev_state_id == current.pdu_id
and new_pdu.prev_state_origin == current.origin):
return None
enum_branches = self._enumerate_state_branches(txn, new_pdu, current)
for branch, prev_state, state in enum_branches:
if not state:
return PduIdTuple(
prev_state.prev_state_id,
prev_state.prev_state_origin
)
return None
def handle_new_state(self, new_pdu): def handle_new_state(self, new_pdu):
"""Actually perform conflict resolution on the new_pdu on the """Actually perform conflict resolution on the new_pdu on the
assumption we have all the pdus required to perform it. assumption we have all the pdus required to perform it.
@ -752,24 +709,11 @@ class StatePduStore(SQLBaseStore):
return is_current return is_current
@classmethod
@log_function @log_function
def _enumerate_state_branches(cls, txn, pdu_a, pdu_b): def _enumerate_state_branches(self, txn, pdu_a, pdu_b):
branch_a = pdu_a branch_a = pdu_a
branch_b = pdu_b branch_b = pdu_b
get_query = (
"SELECT %(fields)s FROM %(pdus)s as p "
"LEFT JOIN %(state)s as s "
"ON p.pdu_id = s.pdu_id AND p.origin = s.origin "
"WHERE p.pdu_id = ? AND p.origin = ? "
) % {
"fields": _pdu_state_joiner.get_fields(
PdusTable="p", StatePdusTable="s"),
"pdus": PdusTable.table_name,
"state": StatePdusTable.table_name,
}
while True: while True:
if (branch_a.pdu_id == branch_b.pdu_id if (branch_a.pdu_id == branch_b.pdu_id
and branch_a.origin == branch_b.origin): and branch_a.origin == branch_b.origin):
@ -801,13 +745,12 @@ class StatePduStore(SQLBaseStore):
branch_a.prev_state_origin branch_a.prev_state_origin
) )
logger.debug("getting branch_a prev %s", pdu_tuple)
txn.execute(get_query, pdu_tuple)
prev_branch = branch_a prev_branch = branch_a
res = txn.fetchone() logger.debug("getting branch_a prev %s", pdu_tuple)
branch_a = PduEntry(*res) if res else None branch_a = self._get_pdu_tuple(txn, *pdu_tuple)
if branch_a:
branch_a = Pdu.from_pdu_tuple(branch_a)
logger.debug("branch_a=%s", branch_a) logger.debug("branch_a=%s", branch_a)
@ -820,14 +763,13 @@ class StatePduStore(SQLBaseStore):
branch_b.prev_state_id, branch_b.prev_state_id,
branch_b.prev_state_origin branch_b.prev_state_origin
) )
txn.execute(get_query, pdu_tuple)
logger.debug("getting branch_b prev %s", pdu_tuple)
prev_branch = branch_b prev_branch = branch_b
res = txn.fetchone() logger.debug("getting branch_b prev %s", pdu_tuple)
branch_b = PduEntry(*res) if res else None branch_b = self._get_pdu_tuple(txn, *pdu_tuple)
if branch_b:
branch_b = Pdu.from_pdu_tuple(branch_b)
logger.debug("branch_b=%s", branch_b) logger.debug("branch_b=%s", branch_b)

View file

@ -18,6 +18,7 @@ from twisted.internet import defer
from ._base import SQLBaseStore from ._base import SQLBaseStore
from synapse.api.constants import Membership from synapse.api.constants import Membership
from synapse.util.logutils import log_function
import logging import logging
@ -29,8 +30,18 @@ class RoomMemberStore(SQLBaseStore):
def _store_room_member_txn(self, txn, event): def _store_room_member_txn(self, txn, event):
"""Store a room member in the database. """Store a room member in the database.
""" """
target_user_id = event.state_key try:
domain = self.hs.parse_userid(target_user_id).domain target_user_id = event.state_key
domain = self.hs.parse_userid(target_user_id).domain
except:
logger.exception("Failed to parse target_user_id=%s", target_user_id)
raise
logger.debug(
"_store_room_member_txn: target_user_id=%s, membership=%s",
target_user_id,
event.membership,
)
self._simple_insert_txn( self._simple_insert_txn(
txn, txn,
@ -51,12 +62,30 @@ class RoomMemberStore(SQLBaseStore):
"VALUES (?, ?)" "VALUES (?, ?)"
) )
txn.execute(sql, (event.room_id, domain)) txn.execute(sql, (event.room_id, domain))
else: elif event.membership != Membership.INVITE:
sql = ( # Check if this was the last person to have left.
"DELETE FROM room_hosts WHERE room_id = ? AND host = ?" member_events = self._get_members_query_txn(
txn,
where_clause="c.room_id = ? AND m.membership = ? AND m.user_id != ?",
where_values=(event.room_id, Membership.JOIN, target_user_id,)
) )
txn.execute(sql, (event.room_id, domain)) joined_domains = set()
for e in member_events:
try:
joined_domains.add(
self.hs.parse_userid(e.state_key).domain
)
except:
# FIXME: How do we deal with invalid user ids in the db?
logger.exception("Invalid user_id: %s", event.state_key)
if domain not in joined_domains:
sql = (
"DELETE FROM room_hosts WHERE room_id = ? AND host = ?"
)
txn.execute(sql, (event.room_id, domain))
@defer.inlineCallbacks @defer.inlineCallbacks
def get_room_member(self, user_id, room_id): def get_room_member(self, user_id, room_id):
@ -146,8 +175,13 @@ class RoomMemberStore(SQLBaseStore):
vals = where_dict.values() vals = where_dict.values()
return self._get_members_query(clause, vals) return self._get_members_query(clause, vals)
@defer.inlineCallbacks
def _get_members_query(self, where_clause, where_values): def _get_members_query(self, where_clause, where_values):
return self._db_pool.runInteraction(
self._get_members_query_txn,
where_clause, where_values
)
def _get_members_query_txn(self, txn, where_clause, where_values):
sql = ( sql = (
"SELECT e.* FROM events as e " "SELECT e.* FROM events as e "
"INNER JOIN room_memberships as m " "INNER JOIN room_memberships as m "
@ -157,12 +191,11 @@ class RoomMemberStore(SQLBaseStore):
"WHERE %s " "WHERE %s "
) % (where_clause,) ) % (where_clause,)
rows = yield self._execute_and_decode(sql, *where_values) txn.execute(sql, where_values)
rows = self.cursor_to_dict(txn)
# logger.debug("_get_members_query Got rows %s", rows) results = self._parse_events_txn(txn, rows)
return results
results = yield self._parse_events(rows)
defer.returnValue(results)
@defer.inlineCallbacks @defer.inlineCallbacks
def user_rooms_intersect(self, user_list): def user_rooms_intersect(self, user_list):

9
synctl
View file

@ -4,7 +4,6 @@ SYNAPSE="synapse/app/homeserver.py"
CONFIGFILE="homeserver.yaml" CONFIGFILE="homeserver.yaml"
PIDFILE="homeserver.pid" PIDFILE="homeserver.pid"
LOGFILE="homeserver.log"
GREEN=$'\e[1;32m' GREEN=$'\e[1;32m'
NORMAL=$'\e[m' NORMAL=$'\e[m'
@ -14,15 +13,13 @@ set -e
case "$1" in case "$1" in
start) start)
if [ ! -f "$CONFIGFILE" ]; then if [ ! -f "$CONFIGFILE" ]; then
echo "No config file found - generating a default one..." echo "No config file found"
$SYNAPSE -c "$CONFIGFILE" --generate-config echo "To generate a config file, run 'python --generate-config'"
echo "Wrote $CONFIGFILE"
echo "You must now edit this file before continuing"
exit 1 exit 1
fi fi
echo -n "Starting ..." echo -n "Starting ..."
$SYNAPSE --daemonize -c "$CONFIGFILE" --pid-file "$PIDFILE" --log-file "$LOGFILE" $SYNAPSE --daemonize -c "$CONFIGFILE" --pid-file "$PIDFILE"
echo "${GREEN}started${NORMAL}" echo "${GREEN}started${NORMAL}"
;; ;;
stop) stop)

View file

@ -1,6 +1,6 @@
from synapse.api.ratelimiting import Ratelimiter from synapse.api.ratelimiting import Ratelimiter
import unittest from tests import unittest
class TestRatelimiter(unittest.TestCase): class TestRatelimiter(unittest.TestCase):

View file

@ -15,7 +15,7 @@
from synapse.api.events import SynapseEvent from synapse.api.events import SynapseEvent
import unittest from tests import unittest
class SynapseTemplateCheckTestCase(unittest.TestCase): class SynapseTemplateCheckTestCase(unittest.TestCase):

View file

@ -14,11 +14,10 @@
# trial imports # trial imports
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest from tests import unittest
# python imports # python imports
from mock import Mock from mock import Mock
import logging
from ..utils import MockHttpResource, MockClock from ..utils import MockHttpResource, MockClock
@ -28,9 +27,6 @@ from synapse.federation.units import Pdu
from synapse.storage.pdu import PduTuple, PduEntry from synapse.storage.pdu import PduTuple, PduEntry
logging.getLogger().addHandler(logging.NullHandler())
def make_pdu(prev_pdus=[], **kwargs): def make_pdu(prev_pdus=[], **kwargs):
"""Provide some default fields for making a PduTuple.""" """Provide some default fields for making a PduTuple."""
pdu_fields = { pdu_fields = {

View file

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from synapse.federation.pdu_codec import ( from synapse.federation.pdu_codec import (
PduCodec, encode_event_id, decode_event_id PduCodec, encode_event_id, decode_event_id

View file

@ -14,11 +14,10 @@
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock from mock import Mock
import logging
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.http.client import HttpClient from synapse.http.client import HttpClient
@ -26,9 +25,6 @@ from synapse.handlers.directory import DirectoryHandler
from synapse.storage.directory import RoomAliasMapping from synapse.storage.directory import RoomAliasMapping
logging.getLogger().addHandler(logging.NullHandler())
class DirectoryHandlers(object): class DirectoryHandlers(object):
def __init__(self, hs): def __init__(self, hs):
self.directory_handler = DirectoryHandler(hs) self.directory_handler = DirectoryHandler(hs)

View file

@ -14,7 +14,7 @@
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest from tests import unittest
from synapse.api.events.room import ( from synapse.api.events.room import (
InviteJoinEvent, MessageEvent, RoomMemberEvent InviteJoinEvent, MessageEvent, RoomMemberEvent
@ -26,12 +26,8 @@ from synapse.federation.units import Pdu
from mock import NonCallableMock, ANY from mock import NonCallableMock, ANY
import logging
from ..utils import get_mock_call_args from ..utils import get_mock_call_args
logging.getLogger().addHandler(logging.NullHandler())
class FederationTestCase(unittest.TestCase): class FederationTestCase(unittest.TestCase):

View file

@ -14,11 +14,10 @@
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer, reactor from twisted.internet import defer, reactor
from mock import Mock, call, ANY from mock import Mock, call, ANY
import logging
import json import json
from ..utils import MockHttpResource, MockClock, DeferredMockCallable from ..utils import MockHttpResource, MockClock, DeferredMockCallable
@ -34,9 +33,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE
ONLINE = PresenceState.ONLINE ONLINE = PresenceState.ONLINE
logging.getLogger().addHandler(logging.NullHandler())
def _expect_edu(destination, edu_type, content, origin="test"): def _expect_edu(destination, edu_type, content, origin="test"):
return { return {
"origin": origin, "origin": origin,
@ -92,7 +88,6 @@ class PresenceStateTestCase(unittest.TestCase):
# Mock the RoomMemberHandler # Mock the RoomMemberHandler
room_member_handler = Mock(spec=[]) room_member_handler = Mock(spec=[])
hs.handlers.room_member_handler = room_member_handler hs.handlers.room_member_handler = room_member_handler
logging.getLogger().debug("Mocking room_member_handler=%r", room_member_handler)
# Some local users to test with # Some local users to test with
self.u_apple = hs.parse_userid("@apple:test") self.u_apple = hs.parse_userid("@apple:test")

View file

@ -16,11 +16,10 @@
"""This file contains tests of the "presence-like" data that is shared between """This file contains tests of the "presence-like" data that is shared between
presence and profiles; namely, the displayname and avatar_url.""" presence and profiles; namely, the displayname and avatar_url."""
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock, call, ANY from mock import Mock, call, ANY
import logging
from ..utils import MockClock from ..utils import MockClock
@ -35,9 +34,6 @@ UNAVAILABLE = PresenceState.UNAVAILABLE
ONLINE = PresenceState.ONLINE ONLINE = PresenceState.ONLINE
logging.getLogger().addHandler(logging.NullHandler())
class MockReplication(object): class MockReplication(object):
def __init__(self): def __init__(self):
self.edu_handlers = {} self.edu_handlers = {}

View file

@ -14,20 +14,16 @@
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock from mock import Mock
import logging
from synapse.api.errors import AuthError from synapse.api.errors import AuthError
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.handlers.profile import ProfileHandler from synapse.handlers.profile import ProfileHandler
logging.getLogger().addHandler(logging.NullHandler())
class ProfileHandlers(object): class ProfileHandlers(object):
def __init__(self, hs): def __init__(self, hs):
self.profile_handler = ProfileHandler(hs) self.profile_handler = ProfileHandler(hs)

View file

@ -15,7 +15,7 @@
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest from tests import unittest
from synapse.api.events.room import ( from synapse.api.events.room import (
InviteJoinEvent, RoomMemberEvent, RoomConfigEvent InviteJoinEvent, RoomMemberEvent, RoomConfigEvent
@ -27,10 +27,6 @@ from synapse.server import HomeServer
from mock import Mock, NonCallableMock from mock import Mock, NonCallableMock
import logging
logging.getLogger().addHandler(logging.NullHandler())
class RoomMemberHandlerTestCase(unittest.TestCase): class RoomMemberHandlerTestCase(unittest.TestCase):

View file

@ -14,12 +14,11 @@
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock, call, ANY from mock import Mock, call, ANY
import json import json
import logging
from ..utils import MockHttpResource, MockClock, DeferredMockCallable from ..utils import MockHttpResource, MockClock, DeferredMockCallable
@ -27,9 +26,6 @@ from synapse.server import HomeServer
from synapse.handlers.typing import TypingNotificationHandler from synapse.handlers.typing import TypingNotificationHandler
logging.getLogger().addHandler(logging.NullHandler())
def _expect_edu(destination, edu_type, content, origin="test"): def _expect_edu(destination, edu_type, content, origin="test"):
return { return {
"origin": origin, "origin": origin,

View file

@ -14,7 +14,7 @@
# limitations under the License. # limitations under the License.
""" Tests REST events for /events paths.""" """ Tests REST events for /events paths."""
from twisted.trial import unittest from tests import unittest
# twisted imports # twisted imports
from twisted.internet import defer from twisted.internet import defer
@ -27,14 +27,12 @@ from synapse.server import HomeServer
# python imports # python imports
import json import json
import logging
from ..utils import MockHttpResource, MemoryDataStore from ..utils import MockHttpResource, MemoryDataStore
from .utils import RestTestCase from .utils import RestTestCase
from mock import Mock, NonCallableMock from mock import Mock, NonCallableMock
logging.getLogger().addHandler(logging.NullHandler())
PATH_PREFIX = "/_matrix/client/api/v1" PATH_PREFIX = "/_matrix/client/api/v1"

View file

@ -15,11 +15,10 @@
"""Tests REST events for /presence paths.""" """Tests REST events for /presence paths."""
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock from mock import Mock
import logging
from ..utils import MockHttpResource from ..utils import MockHttpResource
@ -28,9 +27,6 @@ from synapse.handlers.presence import PresenceHandler
from synapse.server import HomeServer from synapse.server import HomeServer
logging.getLogger().addHandler(logging.NullHandler())
OFFLINE = PresenceState.OFFLINE OFFLINE = PresenceState.OFFLINE
UNAVAILABLE = PresenceState.UNAVAILABLE UNAVAILABLE = PresenceState.UNAVAILABLE
ONLINE = PresenceState.ONLINE ONLINE = PresenceState.ONLINE

View file

@ -15,7 +15,7 @@
"""Tests REST events for /profile paths.""" """Tests REST events for /profile paths."""
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock from mock import Mock
@ -28,6 +28,7 @@ from synapse.server import HomeServer
myid = "@1234ABCD:test" myid = "@1234ABCD:test"
PATH_PREFIX = "/_matrix/client/api/v1" PATH_PREFIX = "/_matrix/client/api/v1"
class ProfileTestCase(unittest.TestCase): class ProfileTestCase(unittest.TestCase):
""" Tests profile management. """ """ Tests profile management. """

View file

@ -17,7 +17,7 @@
from twisted.internet import defer from twisted.internet import defer
# trial imports # trial imports
from twisted.trial import unittest from tests import unittest
from synapse.api.constants import Membership from synapse.api.constants import Membership

View file

@ -14,7 +14,7 @@
# limitations under the License. # limitations under the License.
from twisted.trial import unittest from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from mock import Mock, call from mock import Mock, call

View file

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest
from mock import Mock, patch from mock import Mock, patch

View file

@ -13,23 +13,32 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from tests import unittest
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest from twisted.python.log import PythonLoggingObserver
from synapse.state import StateHandler from synapse.state import StateHandler
from synapse.storage.pdu import PduEntry from synapse.storage.pdu import PduEntry
from synapse.federation.pdu_codec import encode_event_id from synapse.federation.pdu_codec import encode_event_id
from synapse.federation.units import Pdu
from collections import namedtuple from collections import namedtuple
from mock import Mock from mock import Mock
import mock
ReturnType = namedtuple( ReturnType = namedtuple(
"StateReturnType", ["new_branch", "current_branch"] "StateReturnType", ["new_branch", "current_branch"]
) )
def _gen_get_power_level(power_level_list):
def get_power_level(room_id, user_id):
return defer.succeed(power_level_list.get(user_id, None))
return get_power_level
class StateTestCase(unittest.TestCase): class StateTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.persistence = Mock(spec=[ self.persistence = Mock(spec=[
@ -38,6 +47,7 @@ class StateTestCase(unittest.TestCase):
"get_latest_pdus_in_context", "get_latest_pdus_in_context",
"get_current_state_pdu", "get_current_state_pdu",
"get_pdu", "get_pdu",
"get_power_level",
]) ])
self.replication = Mock(spec=["get_pdu"]) self.replication = Mock(spec=["get_pdu"])
@ -51,10 +61,12 @@ class StateTestCase(unittest.TestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_new_state_key(self): def test_new_state_key(self):
# We've never seen anything for this state before # We've never seen anything for this state before
new_pdu = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) new_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u")
self.persistence.get_power_level.side_effect = _gen_get_power_level({})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu], []) (ReturnType([new_pdu], []), None)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -74,11 +86,44 @@ class StateTestCase(unittest.TestCase):
# We do a direct overwriting of the old state, i.e., the new state # We do a direct overwriting of the old state, i.e., the new state
# points to the old state. # points to the old state.
old_pdu = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u1")
new_pdu = new_fake_pdu_entry("B", "test", "mem", "x", "A", 5) new_pdu = new_fake_pdu("B", "test", "mem", "x", "A", "u2")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 5,
})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu, old_pdu], [old_pdu]) (ReturnType([new_pdu, old_pdu], [old_pdu]), None)
)
is_new = yield self.state.handle_new_state(new_pdu)
self.assertTrue(is_new)
self.persistence.get_unresolved_state_tree.assert_called_once_with(
new_pdu
)
self.assertEqual(1, self.persistence.update_current_state.call_count)
self.assertFalse(self.replication.get_pdu.called)
@defer.inlineCallbacks
def test_overwrite(self):
old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1")
old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", "A", "u2")
new_pdu = new_fake_pdu("C", "test", "mem", "x", "B", "u3")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 5,
"u3": 0,
})
self.persistence.get_unresolved_state_tree.return_value = (
(ReturnType([new_pdu, old_pdu_2, old_pdu_1], [old_pdu_1]), None)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -98,12 +143,18 @@ class StateTestCase(unittest.TestCase):
# We try to update the state based on an outdated state, and have a # We try to update the state based on an outdated state, and have a
# too low power level. # too low power level.
old_pdu_1 = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1")
old_pdu_2 = new_fake_pdu_entry("B", "test", "mem", "x", None, 10) old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2")
new_pdu = new_fake_pdu_entry("C", "test", "mem", "x", "A", 5) new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 5,
})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]) (ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -123,12 +174,18 @@ class StateTestCase(unittest.TestCase):
# We try to update the state based on an outdated state, but have # We try to update the state based on an outdated state, but have
# sufficient power level to force the update. # sufficient power level to force the update.
old_pdu_1 = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1")
old_pdu_2 = new_fake_pdu_entry("B", "test", "mem", "x", None, 10) old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2")
new_pdu = new_fake_pdu_entry("C", "test", "mem", "x", "A", 15) new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 15,
})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]) (ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -148,12 +205,18 @@ class StateTestCase(unittest.TestCase):
# We try to update the state based on an outdated state, the power # We try to update the state based on an outdated state, the power
# levels are the same and so are the branch lengths # levels are the same and so are the branch lengths
old_pdu_1 = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1")
old_pdu_2 = new_fake_pdu_entry("B", "test", "mem", "x", None, 10) old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2")
new_pdu = new_fake_pdu_entry("C", "test", "mem", "x", "A", 10) new_pdu = new_fake_pdu("C", "test", "mem", "x", "A", "u3")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 10,
})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]) (ReturnType([new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]), None)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -173,13 +236,26 @@ class StateTestCase(unittest.TestCase):
# We try to update the state based on an outdated state, the power # We try to update the state based on an outdated state, the power
# levels are the same but the branch length of the new one is longer. # levels are the same but the branch length of the new one is longer.
old_pdu_1 = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu_1 = new_fake_pdu("A", "test", "mem", "x", None, "u1")
old_pdu_2 = new_fake_pdu_entry("B", "test", "mem", "x", None, 10) old_pdu_2 = new_fake_pdu("B", "test", "mem", "x", None, "u2")
old_pdu_3 = new_fake_pdu_entry("C", "test", "mem", "x", "A", 10) old_pdu_3 = new_fake_pdu("C", "test", "mem", "x", "A", "u3")
new_pdu = new_fake_pdu_entry("D", "test", "mem", "x", "C", 10) new_pdu = new_fake_pdu("D", "test", "mem", "x", "C", "u4")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 10,
"u4": 10,
})
self.persistence.get_unresolved_state_tree.return_value = ( self.persistence.get_unresolved_state_tree.return_value = (
ReturnType([new_pdu, old_pdu_3, old_pdu_1], [old_pdu_2, old_pdu_1]) (
ReturnType(
[new_pdu, old_pdu_3, old_pdu_1],
[old_pdu_2, old_pdu_1]
),
None
)
) )
is_new = yield self.state.handle_new_state(new_pdu) is_new = yield self.state.handle_new_state(new_pdu)
@ -200,22 +276,38 @@ class StateTestCase(unittest.TestCase):
# triggering a get_pdu request # triggering a get_pdu request
# The pdu we haven't seen # The pdu we haven't seen
old_pdu_1 = new_fake_pdu_entry("A", "test", "mem", "x", None, 10) old_pdu_1 = new_fake_pdu(
"A", "test", "mem", "x", None, "u1", depth=0
)
old_pdu_2 = new_fake_pdu_entry("B", "test", "mem", "x", None, 10) old_pdu_2 = new_fake_pdu(
new_pdu = new_fake_pdu_entry("C", "test", "mem", "x", "A", 20) "B", "test", "mem", "x", "A", "u2", depth=1
)
new_pdu = new_fake_pdu(
"C", "test", "mem", "x", "A", "u3", depth=2
)
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 20,
})
# The return_value of `get_unresolved_state_tree`, which changes after # The return_value of `get_unresolved_state_tree`, which changes after
# the call to get_pdu # the call to get_pdu
tree_to_return = [ReturnType([new_pdu], [old_pdu_2])] tree_to_return = [(ReturnType([new_pdu], [old_pdu_2]), 0)]
def return_tree(p): def return_tree(p):
return tree_to_return[0] return tree_to_return[0]
def set_return_tree(*args, **kwargs): def set_return_tree(destination, pdu_origin, pdu_id, outlier=False):
tree_to_return[0] = ReturnType( tree_to_return[0] = (
[new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1] ReturnType(
[new_pdu, old_pdu_1], [old_pdu_2, old_pdu_1]
),
None
) )
return defer.succeed(None)
self.persistence.get_unresolved_state_tree.side_effect = return_tree self.persistence.get_unresolved_state_tree.side_effect = return_tree
@ -227,6 +319,13 @@ class StateTestCase(unittest.TestCase):
self.assertTrue(is_new) self.assertTrue(is_new)
self.replication.get_pdu.assert_called_with(
destination=new_pdu.origin,
pdu_origin=old_pdu_1.origin,
pdu_id=old_pdu_1.pdu_id,
outlier=True
)
self.persistence.get_unresolved_state_tree.assert_called_with( self.persistence.get_unresolved_state_tree.assert_called_with(
new_pdu new_pdu
) )
@ -237,12 +336,233 @@ class StateTestCase(unittest.TestCase):
self.assertEqual(1, self.persistence.update_current_state.call_count) self.assertEqual(1, self.persistence.update_current_state.call_count)
@defer.inlineCallbacks
def test_missing_pdu_depth_1(self):
# We try to update state against a PDU we haven't yet seen,
# triggering a get_pdu request
# The pdu we haven't seen
old_pdu_1 = new_fake_pdu(
"A", "test", "mem", "x", None, "u1", depth=0
)
old_pdu_2 = new_fake_pdu(
"B", "test", "mem", "x", "A", "u2", depth=2
)
old_pdu_3 = new_fake_pdu(
"C", "test", "mem", "x", "B", "u3", depth=3
)
new_pdu = new_fake_pdu(
"D", "test", "mem", "x", "A", "u4", depth=4
)
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 10,
"u4": 20,
})
# The return_value of `get_unresolved_state_tree`, which changes after
# the call to get_pdu
tree_to_return = [
(
ReturnType([new_pdu], [old_pdu_3]),
0
),
(
ReturnType(
[new_pdu, old_pdu_1], [old_pdu_3]
),
1
),
(
ReturnType(
[new_pdu, old_pdu_1], [old_pdu_3, old_pdu_2, old_pdu_1]
),
None
),
]
to_return = [0]
def return_tree(p):
return tree_to_return[to_return[0]]
def set_return_tree(destination, pdu_origin, pdu_id, outlier=False):
to_return[0] += 1
return defer.succeed(None)
self.persistence.get_unresolved_state_tree.side_effect = return_tree
self.replication.get_pdu.side_effect = set_return_tree
self.persistence.get_pdu.return_value = None
is_new = yield self.state.handle_new_state(new_pdu)
self.assertTrue(is_new)
self.assertEqual(2, self.replication.get_pdu.call_count)
self.replication.get_pdu.assert_has_calls(
[
mock.call(
destination=new_pdu.origin,
pdu_origin=old_pdu_1.origin,
pdu_id=old_pdu_1.pdu_id,
outlier=True
),
mock.call(
destination=old_pdu_3.origin,
pdu_origin=old_pdu_2.origin,
pdu_id=old_pdu_2.pdu_id,
outlier=True
),
]
)
self.persistence.get_unresolved_state_tree.assert_called_with(
new_pdu
)
self.assertEquals(
3, self.persistence.get_unresolved_state_tree.call_count
)
self.assertEqual(1, self.persistence.update_current_state.call_count)
@defer.inlineCallbacks
def test_missing_pdu_depth_2(self):
# We try to update state against a PDU we haven't yet seen,
# triggering a get_pdu request
# The pdu we haven't seen
old_pdu_1 = new_fake_pdu(
"A", "test", "mem", "x", None, "u1", depth=0
)
old_pdu_2 = new_fake_pdu(
"B", "test", "mem", "x", "A", "u2", depth=2
)
old_pdu_3 = new_fake_pdu(
"C", "test", "mem", "x", "B", "u3", depth=3
)
new_pdu = new_fake_pdu(
"D", "test", "mem", "x", "A", "u4", depth=1
)
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 10,
"u2": 10,
"u3": 10,
"u4": 20,
})
# The return_value of `get_unresolved_state_tree`, which changes after
# the call to get_pdu
tree_to_return = [
(
ReturnType([new_pdu], [old_pdu_3]),
1,
),
(
ReturnType(
[new_pdu], [old_pdu_3, old_pdu_2]
),
0,
),
(
ReturnType(
[new_pdu, old_pdu_1], [old_pdu_3, old_pdu_2, old_pdu_1]
),
None
),
]
to_return = [0]
def return_tree(p):
return tree_to_return[to_return[0]]
def set_return_tree(destination, pdu_origin, pdu_id, outlier=False):
to_return[0] += 1
return defer.succeed(None)
self.persistence.get_unresolved_state_tree.side_effect = return_tree
self.replication.get_pdu.side_effect = set_return_tree
self.persistence.get_pdu.return_value = None
is_new = yield self.state.handle_new_state(new_pdu)
self.assertTrue(is_new)
self.assertEqual(2, self.replication.get_pdu.call_count)
self.replication.get_pdu.assert_has_calls(
[
mock.call(
destination=old_pdu_3.origin,
pdu_origin=old_pdu_2.origin,
pdu_id=old_pdu_2.pdu_id,
outlier=True
),
mock.call(
destination=new_pdu.origin,
pdu_origin=old_pdu_1.origin,
pdu_id=old_pdu_1.pdu_id,
outlier=True
),
]
)
self.persistence.get_unresolved_state_tree.assert_called_with(
new_pdu
)
self.assertEquals(
3, self.persistence.get_unresolved_state_tree.call_count
)
self.assertEqual(1, self.persistence.update_current_state.call_count)
@defer.inlineCallbacks
def test_no_common_ancestor(self):
# We do a direct overwriting of the old state, i.e., the new state
# points to the old state.
old_pdu = new_fake_pdu("A", "test", "mem", "x", None, "u1")
new_pdu = new_fake_pdu("B", "test", "mem", "x", None, "u2")
self.persistence.get_power_level.side_effect = _gen_get_power_level({
"u1": 5,
"u2": 10,
})
self.persistence.get_unresolved_state_tree.return_value = (
(ReturnType([new_pdu], [old_pdu]), None)
)
is_new = yield self.state.handle_new_state(new_pdu)
self.assertTrue(is_new)
self.persistence.get_unresolved_state_tree.assert_called_once_with(
new_pdu
)
self.assertEqual(1, self.persistence.update_current_state.call_count)
self.assertFalse(self.replication.get_pdu.called)
@defer.inlineCallbacks @defer.inlineCallbacks
def test_new_event(self): def test_new_event(self):
event = Mock() event = Mock()
event.event_id = "12123123@test" event.event_id = "12123123@test"
state_pdu = new_fake_pdu_entry("C", "test", "mem", "x", "A", 20) state_pdu = new_fake_pdu("C", "test", "mem", "x", "A", 20)
snapshot = Mock() snapshot = Mock()
snapshot.prev_state_pdu = state_pdu snapshot.prev_state_pdu = state_pdu
@ -269,24 +589,25 @@ class StateTestCase(unittest.TestCase):
) )
def new_fake_pdu_entry(pdu_id, context, pdu_type, state_key, prev_state_id, def new_fake_pdu(pdu_id, context, pdu_type, state_key, prev_state_id,
power_level): user_id, depth=0):
new_pdu = PduEntry( new_pdu = Pdu(
pdu_id=pdu_id, pdu_id=pdu_id,
pdu_type=pdu_type, pdu_type=pdu_type,
state_key=state_key, state_key=state_key,
power_level=power_level, user_id=user_id,
prev_state_id=prev_state_id, prev_state_id=prev_state_id,
origin="example.com", origin="example.com",
context="context", context="context",
ts=1405353060021, ts=1405353060021,
depth=0, depth=depth,
content_json="{}", content_json="{}",
unrecognized_keys="{}", unrecognized_keys="{}",
outlier=True, outlier=True,
is_state=True, is_state=True,
prev_state_origin="example.com", prev_state_origin="example.com",
have_processed=True, have_processed=True,
content={},
) )
return new_pdu return new_pdu

View file

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import unittest from tests import unittest
from synapse.server import BaseHomeServer from synapse.server import BaseHomeServer
from synapse.types import UserID, RoomAlias from synapse.types import UserID, RoomAlias

79
tests/unittest.py Normal file
View file

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from twisted.trial import unittest
import logging
# logging doesn't have a "don't log anything at all EVARRRR setting,
# but since the highest value is 50, 1000000 should do ;)
NEVER = 1000000
logging.getLogger().addHandler(logging.StreamHandler())
logging.getLogger().setLevel(NEVER)
def around(target):
"""A CLOS-style 'around' modifier, which wraps the original method of the
given instance with another piece of code.
@around(self)
def method_name(orig, *args, **kwargs):
return orig(*args, **kwargs)
"""
def _around(code):
name = code.__name__
orig = getattr(target, name)
def new(*args, **kwargs):
return code(orig, *args, **kwargs)
setattr(target, name, new)
return _around
class TestCase(unittest.TestCase):
"""A subclass of twisted.trial's TestCase which looks for 'loglevel'
attributes on both itself and its individual test methods, to override the
root logger's logging level while that test (case|method) runs."""
def __init__(self, methodName, *args, **kwargs):
super(TestCase, self).__init__(methodName, *args, **kwargs)
method = getattr(self, methodName)
level = getattr(method, "loglevel",
getattr(self, "loglevel",
NEVER))
@around(self)
def setUp(orig):
old_level = logging.getLogger().level
if old_level != level:
@around(self)
def tearDown(orig):
ret = orig()
logging.getLogger().setLevel(old_level)
return ret
logging.getLogger().setLevel(level)
return orig()
def DEBUG(target):
"""A decorator to set the .loglevel attribute to logging.DEBUG.
Can apply to either a TestCase or an individual test method."""
target.loglevel = logging.DEBUG
return target

View file

@ -15,7 +15,7 @@
from twisted.internet import defer from twisted.internet import defer
from twisted.trial import unittest from tests import unittest
from synapse.util.lockutils import LockManager from synapse.util.lockutils import LockManager
@ -105,4 +105,4 @@ class LockManagerTestCase(unittest.TestCase):
pass pass
with (yield self.lock_manager.lock(key)): with (yield self.lock_manager.lock(key)):
pass pass

View file

@ -21,8 +21,8 @@ limitations under the License.
'use strict'; 'use strict';
angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'eventStreamService']) angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'eventStreamService'])
.controller('MatrixWebClientController', ['$scope', '$location', '$rootScope', '$timeout', '$animate', 'matrixService', 'mPresence', 'eventStreamService', 'matrixPhoneService', .controller('MatrixWebClientController', ['$scope', '$location', '$rootScope', '$timeout', '$animate', 'matrixService', 'mPresence', 'eventStreamService', 'eventHandlerService', 'matrixPhoneService',
function($scope, $location, $rootScope, $timeout, $animate, matrixService, mPresence, eventStreamService, matrixPhoneService) { function($scope, $location, $rootScope, $timeout, $animate, matrixService, mPresence, eventStreamService, eventHandlerService, matrixPhoneService) {
// Check current URL to avoid to display the logout button on the login page // Check current URL to avoid to display the logout button on the login page
$scope.location = $location.path(); $scope.location = $location.path();
@ -73,7 +73,10 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
// Clean permanent data // Clean permanent data
matrixService.setConfig({}); matrixService.setConfig({});
matrixService.saveConfig(); matrixService.saveConfig();
// Reset cached data
eventHandlerService.reset();
// And go to the login page // And go to the login page
$location.url("login"); $location.url("login");
}; };
@ -96,9 +99,14 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
delete roomMembers[matrixService.config().user_id]; delete roomMembers[matrixService.config().user_id];
$rootScope.currentCall.user_id = Object.keys(roomMembers)[0]; $rootScope.currentCall.user_id = Object.keys(roomMembers)[0];
// set it to the user ID until we fetch the display name
$rootScope.currentCall.userProfile = { displayname: $rootScope.currentCall.user_id };
matrixService.getProfile($rootScope.currentCall.user_id).then( matrixService.getProfile($rootScope.currentCall.user_id).then(
function(response) { function(response) {
$rootScope.currentCall.userProfile = response.data; if (response.data.displayname) $rootScope.currentCall.userProfile.displayname = response.data.displayname;
if (response.data.avatar_url) $rootScope.currentCall.userProfile.avatar_url = response.data.avatar_url;
}, },
function(error) { function(error) {
$scope.feedback = "Can't load user profile"; $scope.feedback = "Can't load user profile";
@ -118,7 +126,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
angular.element('#ringAudio')[0].pause(); angular.element('#ringAudio')[0].pause();
angular.element('#ringbackAudio')[0].pause(); angular.element('#ringbackAudio')[0].pause();
angular.element('#callendAudio')[0].play(); angular.element('#callendAudio')[0].play();
} else if (newVal == 'ended' && oldVal == 'invite_sent') { } else if (newVal == 'ended' && oldVal == 'invite_sent' && $rootScope.currentCall.hangupParty == 'remote') {
angular.element('#ringAudio')[0].pause(); angular.element('#ringAudio')[0].pause();
angular.element('#ringbackAudio')[0].pause(); angular.element('#ringbackAudio')[0].pause();
angular.element('#busyAudio')[0].play(); angular.element('#busyAudio')[0].play();
@ -130,39 +138,41 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even
}); });
$rootScope.$on(matrixPhoneService.INCOMING_CALL_EVENT, function(ngEvent, call) { $rootScope.$on(matrixPhoneService.INCOMING_CALL_EVENT, function(ngEvent, call) {
console.trace("incoming call"); console.log("incoming call");
if ($rootScope.currentCall && $rootScope.currentCall.state != 'ended') {
console.log("rejecting call because we're already in a call");
call.hangup();
return;
}
call.onError = $scope.onCallError; call.onError = $scope.onCallError;
call.onHangup = $scope.onCallHangup; call.onHangup = $scope.onCallHangup;
$rootScope.currentCall = call; $rootScope.currentCall = call;
}); });
$rootScope.$on(matrixPhoneService.REPLACED_CALL_EVENT, function(ngEvent, oldCall, newCall) {
console.log("call ID "+oldCall.call_id+" has been replaced by call ID "+newCall.call_id+"!");
newCall.onError = $scope.onCallError;
newCall.onHangup = $scope.onCallHangup;
$rootScope.currentCall = newCall;
});
$scope.answerCall = function() { $scope.answerCall = function() {
$rootScope.currentCall.answer(); $rootScope.currentCall.answer();
}; };
$scope.hangupCall = function() { $scope.hangupCall = function() {
$rootScope.currentCall.hangup(); $rootScope.currentCall.hangup();
$timeout(function() {
var icon = angular.element('#callEndedIcon');
$animate.addClass(icon, 'callIconRotate');
$timeout(function(){
$rootScope.currentCall = undefined;
}, 4070);
}, 100);
}; };
$rootScope.onCallError = function(errStr) { $rootScope.onCallError = function(errStr) {
$scope.feedback = errStr; $scope.feedback = errStr;
} }
$rootScope.onCallHangup = function() { $rootScope.onCallHangup = function(call) {
$timeout(function() { if (call == $rootScope.currentCall) {
var icon = angular.element('#callEndedIcon');
$animate.addClass(icon, 'callIconRotate');
$timeout(function(){ $timeout(function(){
$rootScope.currentCall = undefined; if (call == $rootScope.currentCall) $rootScope.currentCall = undefined;
}, 4070); }, 4070);
}, 100); }
} }
}]); }]);

View file

@ -55,20 +55,20 @@ a:active { color: #000; }
margin-left: 4px; margin-left: 4px;
margin-right: 4px; margin-right: 4px;
margin-top: 8px; margin-top: 8px;
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
} }
.callIconRotate { #callEndedIcon {
-webkit-transform: rotateZ(45deg); transition:all linear 0.5s;
-moz-transform: rotateZ(45deg); }
-ms-transform: rotateZ(45deg);
-o-transform: rotateZ(45deg); #callEndedIcon {
transform: rotateZ(45deg); transform: rotateZ(45deg);
} }
#callEndedIcon.ng-hide {
transform: rotateZ(0deg);
}
#callPeerImage { #callPeerImage {
width: 32px; width: 32px;
height: 32px; height: 32px;
@ -220,12 +220,6 @@ a:active { color: #000; }
height: 100%; height: 100%;
} }
#roomName {
float: right;
font-size: 16px;
margin-top: 15px;
}
#roomHeader { #roomHeader {
margin: auto; margin: auto;
padding-left: 20px; padding-left: 20px;
@ -263,12 +257,80 @@ a:active { color: #000; }
#mainInput { #mainInput {
width: 100%; width: 100%;
resize: none;
} }
.blink { .blink {
background-color: #faa; background-color: #faa;
} }
.roomHighlight {
font-weight: bold;
}
.publicTable {
max-width: 480px;
width: 100%;
border-collapse: collapse;
}
.publicTable tr {
width: 100%;
}
.publicTable td {
vertical-align: text-top;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.publicRoomEntry {
max-width: 430px;
}
.publicRoomJoinedUsers {
width: 5em;
text-align: right;
font-size: 12px;
color: #888;
}
.publicRoomTopic {
color: #888;
font-size: 12px;
overflow: hidden;
padding-bottom: 5px;
border-bottom: 1px #ddd solid;
}
#roomName {
font-size: 16px;
text-align: right;
}
#roomTopic {
font-size: 13px;
text-align: right;
}
.roomNameInput, .roomTopicInput {
width: 100%;
}
.roomNameSection, .roomTopicSection {
float: right;
width: 100%;
}
.roomNameSetNew, .roomTopicSetNew {
float: right;
}
.roomHeaderInfo {
float: right;
margin-top: 15px;
width: 50%;
}
/*** Participant list ***/ /*** Participant list ***/
#usersTableWrapper { #usersTableWrapper {
@ -465,6 +527,10 @@ a:active { color: #000; }
text-align: left ! important; text-align: left ! important;
} }
.bubble .message {
/* Wrap words and break lines on CR+LF */
white-space: pre-wrap;
}
.bubble .messagePending { .bubble .messagePending {
opacity: 0.3 opacity: 0.3
} }

View file

@ -16,6 +16,7 @@ limitations under the License.
var matrixWebClient = angular.module('matrixWebClient', [ var matrixWebClient = angular.module('matrixWebClient', [
'ngRoute', 'ngRoute',
'ngAnimate',
'MatrixWebClientController', 'MatrixWebClientController',
'LoginController', 'LoginController',
'RegisterController', 'RegisterController',

View file

@ -35,26 +35,42 @@ angular.module('eventHandlerService', [])
var POWERLEVEL_EVENT = "POWERLEVEL_EVENT"; var POWERLEVEL_EVENT = "POWERLEVEL_EVENT";
var CALL_EVENT = "CALL_EVENT"; var CALL_EVENT = "CALL_EVENT";
var NAME_EVENT = "NAME_EVENT"; var NAME_EVENT = "NAME_EVENT";
var TOPIC_EVENT = "TOPIC_EVENT";
var RESET_EVENT = "RESET_EVENT"; // eventHandlerService has been resetted
var initialSyncDeferred = $q.defer();
$rootScope.events = {
rooms: {} // will contain roomId: { messages:[], members:{userid1: event} }
};
// used for dedupping events - could be expanded in future... // used for dedupping events - could be expanded in future...
// FIXME: means that we leak memory over time (along with lots of the rest // FIXME: means that we leak memory over time (along with lots of the rest
// of the app, given we never try to reap memory yet) // of the app, given we never try to reap memory yet)
var eventMap = {}; var eventMap = {};
$rootScope.presence = {}; $rootScope.presence = {};
var initialSyncDeferred;
var reset = function() {
initialSyncDeferred = $q.defer();
$rootScope.events = {
rooms: {} // will contain roomId: { messages:[], members:{userid1: event} }
};
$rootScope.presence = {};
eventMap = {};
};
reset();
var initRoom = function(room_id) { var initRoom = function(room_id) {
if (!(room_id in $rootScope.events.rooms)) { if (!(room_id in $rootScope.events.rooms)) {
console.log("Creating new handler entry for " + room_id); console.log("Creating new handler entry for " + room_id);
$rootScope.events.rooms[room_id] = {}; $rootScope.events.rooms[room_id] = {};
$rootScope.events.rooms[room_id].messages = []; $rootScope.events.rooms[room_id].messages = [];
$rootScope.events.rooms[room_id].members = {}; $rootScope.events.rooms[room_id].members = {};
// Pagination information
$rootScope.events.rooms[room_id].pagination = {
earliest_token: "END" // how far back we've paginated
};
} }
}; };
@ -64,9 +80,37 @@ angular.module('eventHandlerService', [])
} }
}; };
var handleRoomCreate = function(event, isLiveEvent) { // Generic method to handle events data
initRoom(event.room_id); var handleRoomDateEvent = function(event, isLiveEvent, addToRoomMessages) {
// Add topic changes as if they were a room message
if (addToRoomMessages) {
if (isLiveEvent) {
$rootScope.events.rooms[event.room_id].messages.push(event);
}
else {
$rootScope.events.rooms[event.room_id].messages.unshift(event);
}
}
// live events always update, but non-live events only update if the
// ts is later.
var latestData = true;
if (!isLiveEvent) {
var eventTs = event.ts;
var storedEvent = $rootScope.events.rooms[event.room_id][event.type];
if (storedEvent) {
if (storedEvent.ts > eventTs) {
// ignore it, we have a newer one already.
latestData = false;
}
}
}
if (latestData) {
$rootScope.events.rooms[event.room_id][event.type] = event;
}
};
var handleRoomCreate = function(event, isLiveEvent) {
// For now, we do not use the event data. Simply signal it to the app controllers // For now, we do not use the event data. Simply signal it to the app controllers
$rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent); $rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent);
}; };
@ -76,13 +120,18 @@ angular.module('eventHandlerService', [])
}; };
var handleMessage = function(event, isLiveEvent) { var handleMessage = function(event, isLiveEvent) {
initRoom(event.room_id);
if (isLiveEvent) { if (isLiveEvent) {
if (event.user_id === matrixService.config().user_id && if (event.user_id === matrixService.config().user_id &&
(event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) { (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) {
// assume we've already echoed it // Assume we've already echoed it. So, there is a fake event in the messages list of the room
// FIXME: track events by ID and ungrey the right message to show it's been delivered // Replace this fake event by the true one
var index = getRoomEventIndex(event.room_id, event.event_id);
if (index) {
$rootScope.events.rooms[event.room_id].messages[index] = event;
}
else {
$rootScope.events.rooms[event.room_id].messages.push(event);
}
} }
else { else {
$rootScope.events.rooms[event.room_id].messages.push(event); $rootScope.events.rooms[event.room_id].messages.push(event);
@ -100,9 +149,7 @@ angular.module('eventHandlerService', [])
$rootScope.$broadcast(MSG_EVENT, event, isLiveEvent); $rootScope.$broadcast(MSG_EVENT, event, isLiveEvent);
}; };
var handleRoomMember = function(event, isLiveEvent) { var handleRoomMember = function(event, isLiveEvent, isStateEvent) {
initRoom(event.room_id);
// if the server is stupidly re-relaying a no-op join, discard it. // if the server is stupidly re-relaying a no-op join, discard it.
if (event.prev_content && if (event.prev_content &&
event.content.membership === "join" && event.content.membership === "join" &&
@ -112,7 +159,10 @@ angular.module('eventHandlerService', [])
} }
// add membership changes as if they were a room message if something interesting changed // add membership changes as if they were a room message if something interesting changed
if (event.content.prev !== event.content.membership) { // Exception: Do not do this if the event is a room state event because such events already come
// as room messages events. Moreover, when they come as room messages events, they are relatively ordered
// with other other room messages
if (event.content.prev !== event.content.membership && !isStateEvent) {
if (isLiveEvent) { if (isLiveEvent) {
$rootScope.events.rooms[event.room_id].messages.push(event); $rootScope.events.rooms[event.room_id].messages.push(event);
} }
@ -121,8 +171,13 @@ angular.module('eventHandlerService', [])
} }
} }
$rootScope.events.rooms[event.room_id].members[event.state_key] = event; // Use data from state event or the latest data from the stream.
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); // Do not care of events that come when paginating back
if (isStateEvent || isLiveEvent) {
$rootScope.events.rooms[event.room_id].members[event.state_key] = event;
}
$rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent, isStateEvent);
}; };
var handlePresence = function(event, isLiveEvent) { var handlePresence = function(event, isLiveEvent) {
@ -131,8 +186,6 @@ angular.module('eventHandlerService', [])
}; };
var handlePowerLevels = function(event, isLiveEvent) { var handlePowerLevels = function(event, isLiveEvent) {
initRoom(event.room_id);
// Keep the latest data. Do not care of events that come when paginating back // Keep the latest data. Do not care of events that come when paginating back
if (!$rootScope.events.rooms[event.room_id][event.type] || isLiveEvent) { if (!$rootScope.events.rooms[event.room_id][event.type] || isLiveEvent) {
$rootScope.events.rooms[event.room_id][event.type] = event; $rootScope.events.rooms[event.room_id][event.type] = event;
@ -140,17 +193,48 @@ angular.module('eventHandlerService', [])
} }
}; };
var handleRoomName = function(event, isLiveEvent) { var handleRoomName = function(event, isLiveEvent, isStateEvent) {
console.log("handleRoomName " + isLiveEvent); console.log("handleRoomName room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - name: " + event.content.name);
handleRoomDateEvent(event, isLiveEvent, !isStateEvent);
initRoom(event.room_id);
$rootScope.events.rooms[event.room_id][event.type] = event;
$rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent);
}; };
var handleRoomTopic = function(event, isLiveEvent, isStateEvent) {
console.log("handleRoomTopic room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - topic: " + event.content.topic);
handleRoomDateEvent(event, isLiveEvent, !isStateEvent);
$rootScope.$broadcast(TOPIC_EVENT, event, isLiveEvent);
};
var handleCallEvent = function(event, isLiveEvent) { var handleCallEvent = function(event, isLiveEvent) {
$rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent);
if (event.type === 'm.call.invite') {
$rootScope.events.rooms[event.room_id].messages.push(event);
}
};
/**
* Get the index of the event in $rootScope.events.rooms[room_id].messages
* @param {type} room_id the room id
* @param {type} event_id the event id to look for
* @returns {Number | undefined} the index. undefined if not found.
*/
var getRoomEventIndex = function(room_id, event_id) {
var index;
var room = $rootScope.events.rooms[room_id];
if (room) {
// Start looking from the tail since the first goal of this function
// is to find a messaged among the latest ones
for (var i = room.messages.length - 1; i > 0; i--) {
var message = room.messages[i];
if (event_id === message.event_id) {
index = i;
break;
}
}
}
return index;
}; };
return { return {
@ -161,19 +245,37 @@ angular.module('eventHandlerService', [])
POWERLEVEL_EVENT: POWERLEVEL_EVENT, POWERLEVEL_EVENT: POWERLEVEL_EVENT,
CALL_EVENT: CALL_EVENT, CALL_EVENT: CALL_EVENT,
NAME_EVENT: NAME_EVENT, NAME_EVENT: NAME_EVENT,
TOPIC_EVENT: TOPIC_EVENT,
RESET_EVENT: RESET_EVENT,
reset: function() {
reset();
$rootScope.$broadcast(RESET_EVENT);
},
handleEvent: function(event, isLiveEvent) { handleEvent: function(event, isLiveEvent, isStateEvent) {
// FIXME: event duplication suppression is all broken as the code currently expect to handles
// events multiple times to get their side-effects... // FIXME: /initialSync on a particular room is not yet available
/* // So initRoom on a new room is not called. Make sure the room data is initialised here
if (eventMap[event.event_id]) { initRoom(event.room_id);
console.log("discarding duplicate event: " + JSON.stringify(event));
return; // Avoid duplicated events
// Needed for rooms where initialSync has not been done.
// In this case, we do not know where to start pagination. So, it starts from the END
// and we can have the same event (ex: joined, invitation) coming from the pagination
// AND from the event stream.
// FIXME: This workaround should be no more required when /initialSync on a particular room
// will be available (as opposite to the global /initialSync done at startup)
if (!isStateEvent) { // Do not consider state events
if (event.event_id && eventMap[event.event_id]) {
console.log("discarding duplicate event: " + JSON.stringify(event, undefined, 4));
return;
}
else {
eventMap[event.event_id] = 1;
}
} }
else {
eventMap[event.event_id] = 1;
}
*/
if (event.type.indexOf('m.call.') === 0) { if (event.type.indexOf('m.call.') === 0) {
handleCallEvent(event, isLiveEvent); handleCallEvent(event, isLiveEvent);
} }
@ -189,7 +291,7 @@ angular.module('eventHandlerService', [])
handleMessage(event, isLiveEvent); handleMessage(event, isLiveEvent);
break; break;
case "m.room.member": case "m.room.member":
handleRoomMember(event, isLiveEvent); handleRoomMember(event, isLiveEvent, isStateEvent);
break; break;
case "m.presence": case "m.presence":
handlePresence(event, isLiveEvent); handlePresence(event, isLiveEvent);
@ -202,7 +304,10 @@ angular.module('eventHandlerService', [])
handlePowerLevels(event, isLiveEvent); handlePowerLevels(event, isLiveEvent);
break; break;
case 'm.room.name': case 'm.room.name':
handleRoomName(event, isLiveEvent); handleRoomName(event, isLiveEvent, isStateEvent);
break;
case 'm.room.topic':
handleRoomTopic(event, isLiveEvent, isStateEvent);
break; break;
default: default:
console.log("Unable to handle event type " + event.type); console.log("Unable to handle event type " + event.type);
@ -214,12 +319,22 @@ angular.module('eventHandlerService', [])
// isLiveEvents determines whether notifications should be shown, whether // isLiveEvents determines whether notifications should be shown, whether
// messages get appended to the start/end of lists, etc. // messages get appended to the start/end of lists, etc.
handleEvents: function(events, isLiveEvents) { handleEvents: function(events, isLiveEvents, isStateEvents) {
for (var i=0; i<events.length; i++) { for (var i=0; i<events.length; i++) {
this.handleEvent(events[i], isLiveEvents); this.handleEvent(events[i], isLiveEvents, isStateEvents);
} }
}, },
// Handle messages from /initialSync or /messages
handleRoomMessages: function(room_id, messages, isLiveEvents) {
initRoom(room_id);
this.handleEvents(messages.chunk, isLiveEvents);
// Store how far back we've paginated
// This assumes the paginations requests are contiguous and in reverse chronological order
$rootScope.events.rooms[room_id].pagination.earliest_token = messages.end;
},
handleInitialSyncDone: function(initialSyncData) { handleInitialSyncDone: function(initialSyncData) {
console.log("# handleInitialSyncDone"); console.log("# handleInitialSyncDone");
initialSyncDeferred.resolve(initialSyncData); initialSyncDeferred.resolve(initialSyncData);

View file

@ -104,15 +104,19 @@ angular.module('eventStreamService', [])
settings.isActive = true; settings.isActive = true;
var deferred = $q.defer(); var deferred = $q.defer();
// FIXME: We are discarding all the messages. // Initial sync: get all information and the last message of all rooms of the user
matrixService.initialSync(1, false).then( matrixService.initialSync(1, false).then(
function(response) { function(response) {
var rooms = response.data.rooms; var rooms = response.data.rooms;
for (var i = 0; i < rooms.length; ++i) { for (var i = 0; i < rooms.length; ++i) {
var room = rooms[i]; var room = rooms[i];
// console.log("got room: " + room.room_id);
if ("messages" in room) {
eventHandlerService.handleRoomMessages(room.room_id, room.messages, false);
}
if ("state" in room) { if ("state" in room) {
eventHandlerService.handleEvents(room.state, false); eventHandlerService.handleEvents(room.state, false, true);
} }
} }
@ -122,6 +126,7 @@ angular.module('eventStreamService', [])
// Initial sync is done // Initial sync is done
eventHandlerService.handleInitialSyncDone(response); eventHandlerService.handleInitialSyncDone(response);
// Start event streaming from that point
settings.from = response.data.end; settings.from = response.data.end;
doEventStream(deferred); doEventStream(deferred);
}, },

View file

@ -35,86 +35,115 @@ var forAllTracksOnStream = function(s, f) {
forAllAudioTracksOnStream(s, f); forAllAudioTracksOnStream(s, f);
} }
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection; // but not mozRTCPeerConnection because its interface is not compatible
window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription;
window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate;
angular.module('MatrixCall', []) angular.module('MatrixCall', [])
.factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope) { .factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', '$timeout', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope, $timeout) {
var MatrixCall = function(room_id) { var MatrixCall = function(room_id) {
this.room_id = room_id; this.room_id = room_id;
this.call_id = "c" + new Date().getTime(); this.call_id = "c" + new Date().getTime();
this.state = 'fledgling'; this.state = 'fledgling';
this.didConnect = false; this.didConnect = false;
// a queue for candidates waiting to go out. We try to amalgamate candidates into a single candidate message where possible
this.candidateSendQueue = [];
this.candidateSendTries = 0;
} }
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; MatrixCall.prototype.createPeerConnection = function() {
var stunServer = 'stun:stun.l.google.com:19302';
var pc;
if (window.mozRTCPeerConnection) {
pc = window.mozRTCPeerConnection({'url': stunServer});
} else {
pc = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]});
}
var self = this;
pc.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); };
pc.onsignalingstatechange = function() { self.onSignallingStateChanged(); };
pc.onicecandidate = function(c) { self.gotLocalIceCandidate(c); };
pc.onaddstream = function(s) { self.onAddStream(s); };
return pc;
}
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; MatrixCall.prototype.placeCall = function(config) {
var self = this;
MatrixCall.prototype.placeCall = function() {
self = this;
matrixPhoneService.callPlaced(this); matrixPhoneService.callPlaced(this);
navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForInvite(s); }, function(e) { self.getUserMediaFailed(e); }); navigator.getUserMedia({audio: config.audio, video: config.video}, function(s) { self.gotUserMediaForInvite(s); }, function(e) { self.getUserMediaFailed(e); });
self.state = 'wait_local_media'; this.state = 'wait_local_media';
this.direction = 'outbound'; this.direction = 'outbound';
this.config = config;
}; };
MatrixCall.prototype.initWithInvite = function(msg) { MatrixCall.prototype.initWithInvite = function(msg) {
this.msg = msg; this.msg = msg;
this.peerConn = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}) this.peerConn = this.createPeerConnection();
self= this; this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError);
this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); };
this.peerConn.onicecandidate = function(c) { self.gotLocalIceCandidate(c); };
this.peerConn.onsignalingstatechange = function() { self.onSignallingStateChanged(); };
this.peerConn.onaddstream = function(s) { self.onAddStream(s); };
this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), self.onSetRemoteDescriptionSuccess, self.onSetRemoteDescriptionError);
this.state = 'ringing'; this.state = 'ringing';
this.direction = 'inbound'; this.direction = 'inbound';
}; };
MatrixCall.prototype.answer = function() { MatrixCall.prototype.answer = function() {
console.trace("Answering call "+this.call_id); console.log("Answering call "+this.call_id);
self = this; var self = this;
navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForAnswer(s); }, function(e) { self.getUserMediaFailed(e); }); if (!this.localAVStream && !this.waitForLocalAVStream) {
this.state = 'wait_local_media'; navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForAnswer(s); }, function(e) { self.getUserMediaFailed(e); });
this.state = 'wait_local_media';
} else if (this.localAVStream) {
this.gotUserMediaForAnswer(this.localAVStream);
} else if (this.waitForLocalAVStream) {
this.state = 'wait_local_media';
}
}; };
MatrixCall.prototype.stopAllMedia = function() { MatrixCall.prototype.stopAllMedia = function() {
if (this.localAVStream) { if (this.localAVStream) {
forAllTracksOnStream(this.localAVStream, function(t) { forAllTracksOnStream(this.localAVStream, function(t) {
t.stop(); if (t.stop) t.stop();
}); });
} }
if (this.remoteAVStream) { if (this.remoteAVStream) {
forAllTracksOnStream(this.remoteAVStream, function(t) { forAllTracksOnStream(this.remoteAVStream, function(t) {
t.stop(); if (t.stop) t.stop();
}); });
} }
}; };
MatrixCall.prototype.hangup = function() { MatrixCall.prototype.hangup = function(suppressEvent) {
console.trace("Ending call "+this.call_id); console.log("Ending call "+this.call_id);
this.stopAllMedia(); this.stopAllMedia();
if (this.peerConn) this.peerConn.close();
this.hangupParty = 'local';
var content = { var content = {
version: 0, version: 0,
call_id: this.call_id, call_id: this.call_id,
}; };
matrixService.sendEvent(this.room_id, 'm.call.hangup', undefined, content).then(this.messageSent, this.messageSendFailed); this.sendEventWithRetry('m.call.hangup', content);
this.state = 'ended'; this.state = 'ended';
if (this.onHangup && !suppressEvent) this.onHangup(this);
}; };
MatrixCall.prototype.gotUserMediaForInvite = function(stream) { MatrixCall.prototype.gotUserMediaForInvite = function(stream) {
if (this.successor) {
this.successor.gotUserMediaForAnswer(stream);
return;
}
if (this.state == 'ended') return;
this.localAVStream = stream; this.localAVStream = stream;
var audioTracks = stream.getAudioTracks(); var audioTracks = stream.getAudioTracks();
for (var i = 0; i < audioTracks.length; i++) { for (var i = 0; i < audioTracks.length; i++) {
audioTracks[i].enabled = true; audioTracks[i].enabled = true;
} }
this.peerConn = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}) this.peerConn = this.createPeerConnection();
self = this;
this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); };
this.peerConn.onsignalingstatechange = function() { self.onSignallingStateChanged(); };
this.peerConn.onicecandidate = function(c) { self.gotLocalIceCandidate(c); };
this.peerConn.onaddstream = function(s) { self.onAddStream(s); };
this.peerConn.addStream(stream); this.peerConn.addStream(stream);
var self = this;
this.peerConn.createOffer(function(d) { this.peerConn.createOffer(function(d) {
self.gotLocalOffer(d); self.gotLocalOffer(d);
}, function(e) { }, function(e) {
@ -126,13 +155,15 @@ angular.module('MatrixCall', [])
}; };
MatrixCall.prototype.gotUserMediaForAnswer = function(stream) { MatrixCall.prototype.gotUserMediaForAnswer = function(stream) {
if (this.state == 'ended') return;
this.localAVStream = stream; this.localAVStream = stream;
var audioTracks = stream.getAudioTracks(); var audioTracks = stream.getAudioTracks();
for (var i = 0; i < audioTracks.length; i++) { for (var i = 0; i < audioTracks.length; i++) {
audioTracks[i].enabled = true; audioTracks[i].enabled = true;
} }
this.peerConn.addStream(stream); this.peerConn.addStream(stream);
self = this; var self = this;
var constraints = { var constraints = {
'mandatory': { 'mandatory': {
'OfferToReceiveAudio': true, 'OfferToReceiveAudio': true,
@ -140,25 +171,23 @@ angular.module('MatrixCall', [])
}, },
}; };
this.peerConn.createAnswer(function(d) { self.createdAnswer(d); }, function(e) {}, constraints); this.peerConn.createAnswer(function(d) { self.createdAnswer(d); }, function(e) {}, constraints);
$rootScope.$apply(function() { // This can't be in an apply() because it's called by a predecessor call under glare conditions :(
self.state = 'create_answer'; self.state = 'create_answer';
});
}; };
MatrixCall.prototype.gotLocalIceCandidate = function(event) { MatrixCall.prototype.gotLocalIceCandidate = function(event) {
console.trace(event); console.log(event);
if (event.candidate) { if (event.candidate) {
var content = { this.sendCandidate(event.candidate);
version: 0,
call_id: this.call_id,
candidate: event.candidate
};
matrixService.sendEvent(this.room_id, 'm.call.candidate', undefined, content).then(this.messageSent, this.messageSendFailed);
} }
} }
MatrixCall.prototype.gotRemoteIceCandidate = function(cand) { MatrixCall.prototype.gotRemoteIceCandidate = function(cand) {
console.trace("Got ICE candidate from remote: "+cand); console.log("Got ICE candidate from remote: "+cand);
if (this.state == 'ended') {
console.log("Ignoring remote ICE candidate because call has ended");
return;
}
var candidateObject = new RTCIceCandidate({ var candidateObject = new RTCIceCandidate({
sdpMLineIndex: cand.label, sdpMLineIndex: cand.label,
candidate: cand.candidate candidate: cand.candidate
@ -167,12 +196,18 @@ angular.module('MatrixCall', [])
}; };
MatrixCall.prototype.receivedAnswer = function(msg) { MatrixCall.prototype.receivedAnswer = function(msg) {
this.peerConn.setRemoteDescription(new RTCSessionDescription(msg.answer), self.onSetRemoteDescriptionSuccess, self.onSetRemoteDescriptionError); this.peerConn.setRemoteDescription(new RTCSessionDescription(msg.answer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError);
this.state = 'connecting'; this.state = 'connecting';
}; };
MatrixCall.prototype.gotLocalOffer = function(description) { MatrixCall.prototype.gotLocalOffer = function(description) {
console.trace("Created offer: "+description); console.log("Created offer: "+description);
if (this.state == 'ended') {
console.log("Ignoring newly created offer on call ID "+this.call_id+" because the call has ended");
return;
}
this.peerConn.setLocalDescription(description); this.peerConn.setLocalDescription(description);
var content = { var content = {
@ -180,49 +215,44 @@ angular.module('MatrixCall', [])
call_id: this.call_id, call_id: this.call_id,
offer: description offer: description
}; };
matrixService.sendEvent(this.room_id, 'm.call.invite', undefined, content).then(this.messageSent, this.messageSendFailed); this.sendEventWithRetry('m.call.invite', content);
self = this; var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'invite_sent'; self.state = 'invite_sent';
}); });
}; };
MatrixCall.prototype.createdAnswer = function(description) { MatrixCall.prototype.createdAnswer = function(description) {
console.trace("Created answer: "+description); console.log("Created answer: "+description);
this.peerConn.setLocalDescription(description); this.peerConn.setLocalDescription(description);
var content = { var content = {
version: 0, version: 0,
call_id: this.call_id, call_id: this.call_id,
answer: description answer: description
}; };
matrixService.sendEvent(this.room_id, 'm.call.answer', undefined, content).then(this.messageSent, this.messageSendFailed); this.sendEventWithRetry('m.call.answer', content);
self = this; var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'connecting'; self.state = 'connecting';
}); });
}; };
MatrixCall.prototype.messageSent = function() {
};
MatrixCall.prototype.messageSendFailed = function(error) {
};
MatrixCall.prototype.getLocalOfferFailed = function(error) { MatrixCall.prototype.getLocalOfferFailed = function(error) {
this.onError("Failed to start audio for call!"); this.onError("Failed to start audio for call!");
}; };
MatrixCall.prototype.getUserMediaFailed = function() { MatrixCall.prototype.getUserMediaFailed = function() {
this.onError("Couldn't start capturing audio! Is your microphone set up?"); this.onError("Couldn't start capturing audio! Is your microphone set up?");
this.hangup();
}; };
MatrixCall.prototype.onIceConnectionStateChanged = function() { MatrixCall.prototype.onIceConnectionStateChanged = function() {
if (this.state == 'ended') return; // because ICE can still complete as we're ending the call if (this.state == 'ended') return; // because ICE can still complete as we're ending the call
console.trace("Ice connection state changed to: "+this.peerConn.iceConnectionState); console.log("Ice connection state changed to: "+this.peerConn.iceConnectionState);
// ideally we'd consider the call to be connected when we get media but chrome doesn't implement nay of the 'onstarted' events yet // ideally we'd consider the call to be connected when we get media but chrome doesn't implement nay of the 'onstarted' events yet
if (this.peerConn.iceConnectionState == 'completed' || this.peerConn.iceConnectionState == 'connected') { if (this.peerConn.iceConnectionState == 'completed' || this.peerConn.iceConnectionState == 'connected') {
self = this; var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'connected'; self.state = 'connected';
self.didConnect = true; self.didConnect = true;
@ -231,19 +261,19 @@ angular.module('MatrixCall', [])
}; };
MatrixCall.prototype.onSignallingStateChanged = function() { MatrixCall.prototype.onSignallingStateChanged = function() {
console.trace("Signalling state changed to: "+this.peerConn.signalingState); console.log("call "+this.call_id+": Signalling state changed to: "+this.peerConn.signalingState);
}; };
MatrixCall.prototype.onSetRemoteDescriptionSuccess = function() { MatrixCall.prototype.onSetRemoteDescriptionSuccess = function() {
console.trace("Set remote description"); console.log("Set remote description");
}; };
MatrixCall.prototype.onSetRemoteDescriptionError = function(e) { MatrixCall.prototype.onSetRemoteDescriptionError = function(e) {
console.trace("Failed to set remote description"+e); console.log("Failed to set remote description"+e);
}; };
MatrixCall.prototype.onAddStream = function(event) { MatrixCall.prototype.onAddStream = function(event) {
console.trace("Stream added"+event); console.log("Stream added"+event);
var s = event.stream; var s = event.stream;
@ -264,32 +294,127 @@ angular.module('MatrixCall', [])
}; };
MatrixCall.prototype.onRemoteStreamStarted = function(event) { MatrixCall.prototype.onRemoteStreamStarted = function(event) {
self = this; var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'connected'; self.state = 'connected';
}); });
}; };
MatrixCall.prototype.onRemoteStreamEnded = function(event) { MatrixCall.prototype.onRemoteStreamEnded = function(event) {
self = this; console.log("Remote stream ended");
var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'ended'; self.state = 'ended';
self.hangupParty = 'remote';
self.stopAllMedia(); self.stopAllMedia();
self.onHangup(); if (self.peerConn.signalingState != 'closed') self.peerConn.close();
if (self.onHangup) self.onHangup(self);
}); });
}; };
MatrixCall.prototype.onRemoteStreamTrackStarted = function(event) { MatrixCall.prototype.onRemoteStreamTrackStarted = function(event) {
self = this; var self = this;
$rootScope.$apply(function() { $rootScope.$apply(function() {
self.state = 'connected'; self.state = 'connected';
}); });
}; };
MatrixCall.prototype.onHangupReceived = function() { MatrixCall.prototype.onHangupReceived = function() {
console.log("Hangup received");
this.state = 'ended'; this.state = 'ended';
this.hangupParty = 'remote';
this.stopAllMedia(); this.stopAllMedia();
this.onHangup(); if (this.peerConn.signalingState != 'closed') this.peerConn.close();
if (this.onHangup) this.onHangup(this);
};
MatrixCall.prototype.replacedBy = function(newCall) {
console.log(this.call_id+" being replaced by "+newCall.call_id);
if (this.state == 'wait_local_media') {
console.log("Telling new call to wait for local media");
newCall.waitForLocalAVStream = true;
} else if (this.state == 'create_offer') {
console.log("Handing local stream to new call");
newCall.localAVStream = this.localAVStream;
delete(this.localAVStream);
} else if (this.state == 'invite_sent') {
console.log("Handing local stream to new call");
newCall.localAVStream = this.localAVStream;
delete(this.localAVStream);
}
this.successor = newCall;
this.hangup(true);
};
MatrixCall.prototype.sendEventWithRetry = function(evType, content) {
var ev = { type:evType, content:content, tries:1 };
var self = this;
matrixService.sendEvent(this.room_id, evType, undefined, content).then(this.eventSent, function(error) { self.eventSendFailed(ev, error); } );
};
MatrixCall.prototype.eventSent = function() {
};
MatrixCall.prototype.eventSendFailed = function(ev, error) {
if (ev.tries > 5) {
console.log("Failed to send event of type "+ev.type+" on attempt "+ev.tries+". Giving up.");
return;
}
var delayMs = 500 * Math.pow(2, ev.tries);
console.log("Failed to send event of type "+ev.type+". Retrying in "+delayMs+"ms");
++ev.tries;
var self = this;
$timeout(function() {
matrixService.sendEvent(self.room_id, ev.type, undefined, ev.content).then(self.eventSent, function(error) { self.eventSendFailed(ev, error); } );
}, delayMs);
};
// Sends candidates with are sent in a special way because we try to amalgamate them into one message
MatrixCall.prototype.sendCandidate = function(content) {
this.candidateSendQueue.push(content);
var self = this;
if (this.candidateSendTries == 0) $timeout(function() { self.sendCandidateQueue(); }, 100);
};
MatrixCall.prototype.sendCandidateQueue = function(content) {
if (this.candidateSendQueue.length == 0) return;
var cands = this.candidateSendQueue;
this.candidateSendQueue = [];
++this.candidateSendTries;
var content = {
version: 0,
call_id: this.call_id,
candidates: cands
};
var self = this;
console.log("Attempting to send "+cands.length+" candidates");
matrixService.sendEvent(self.room_id, 'm.call.candidates', undefined, content).then(function() { self.candsSent(); }, function(error) { self.candsSendFailed(cands, error); } );
};
MatrixCall.prototype.candsSent = function() {
this.candidateSendTries = 0;
this.sendCandidateQueue();
};
MatrixCall.prototype.candsSendFailed = function(cands, error) {
for (var i = 0; i < cands.length; ++i) {
this.candidateSendQueue.push(cands[i]);
}
if (this.candidateSendTries > 5) {
console.log("Failed to send candidates on attempt "+ev.tries+". Giving up for now.");
this.candidateSendTries = 0;
return;
}
var delayMs = 500 * Math.pow(2, this.candidateSendTries);
++this.candidateSendTries;
console.log("Failed to send candidates. Retrying in "+delayMs+"ms");
var self = this;
$timeout(function() {
self.sendCandidateQueue();
}, delayMs);
}; };
return MatrixCall; return MatrixCall;

View file

@ -31,15 +31,17 @@ angular.module('matrixFilter', [])
} }
if (undefined === roomName) { if (undefined === roomName) {
// Else, build the name from its users
var room = $rootScope.events.rooms[room_id]; var room = $rootScope.events.rooms[room_id];
if (room) { if (room) {
// Get name from room state date
var room_name_event = room["m.room.name"]; var room_name_event = room["m.room.name"];
if (room_name_event) { if (room_name_event) {
roomName = room_name_event.content.name; roomName = room_name_event.content.name;
} }
else if (room.members) { else if (room.members) {
// Else, build the name from its users
// FIXME: Is it still required?
// Limit the room renaming to 1:1 room // Limit the room renaming to 1:1 room
if (2 === Object.keys(room.members).length) { if (2 === Object.keys(room.members).length) {
for (var i in room.members) { for (var i in room.members) {

View file

@ -22,6 +22,7 @@ angular.module('matrixPhoneService', [])
}; };
matrixPhoneService.INCOMING_CALL_EVENT = "INCOMING_CALL_EVENT"; matrixPhoneService.INCOMING_CALL_EVENT = "INCOMING_CALL_EVENT";
matrixPhoneService.REPLACED_CALL_EVENT = "REPLACED_CALL_EVENT";
matrixPhoneService.allCalls = {}; matrixPhoneService.allCalls = {};
matrixPhoneService.callPlaced = function(call) { matrixPhoneService.callPlaced = function(call) {
@ -38,29 +39,61 @@ angular.module('matrixPhoneService', [])
call.call_id = msg.call_id; call.call_id = msg.call_id;
call.initWithInvite(msg); call.initWithInvite(msg);
matrixPhoneService.allCalls[call.call_id] = call; matrixPhoneService.allCalls[call.call_id] = call;
$rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
// Were we trying to call that user (room)?
var existingCall;
var callIds = Object.keys(matrixPhoneService.allCalls);
for (var i = 0; i < callIds.length; ++i) {
var thisCallId = callIds[i];
var thisCall = matrixPhoneService.allCalls[thisCallId];
if (call.room_id == thisCall.room_id && thisCall.direction == 'outbound'
&& (thisCall.state == 'wait_local_media' || thisCall.state == 'create_offer' || thisCall.state == 'invite_sent')) {
existingCall = thisCall;
break;
}
}
if (existingCall) {
// If we've only got to wait_local_media or create_offer and we've got an invite,
// pick the incoming call because we know we haven't sent our invite yet
// otherwise, pick whichever call has the lowest call ID (by string comparison)
if (existingCall.state == 'wait_local_media' || existingCall.state == 'create_offer' || existingCall.call_id > call.call_id) {
console.log("Glare detected: answering incoming call "+call.call_id+" and canceling outgoing call "+existingCall.call_id);
existingCall.replacedBy(call);
call.answer();
$rootScope.$broadcast(matrixPhoneService.REPLACED_CALL_EVENT, existingCall, call);
} else {
console.log("Glare detected: rejecting incoming call "+call.call_id+" and keeping outgoing call "+existingCall.call_id);
call.hangup();
}
} else {
$rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
}
} else if (event.type == 'm.call.answer') { } else if (event.type == 'm.call.answer') {
var call = matrixPhoneService.allCalls[msg.call_id]; var call = matrixPhoneService.allCalls[msg.call_id];
if (!call) { if (!call) {
console.trace("Got answer for unknown call ID "+msg.call_id); console.log("Got answer for unknown call ID "+msg.call_id);
return; return;
} }
call.receivedAnswer(msg); call.receivedAnswer(msg);
} else if (event.type == 'm.call.candidate') { } else if (event.type == 'm.call.candidates') {
var call = matrixPhoneService.allCalls[msg.call_id]; var call = matrixPhoneService.allCalls[msg.call_id];
if (!call) { if (!call) {
console.trace("Got candidate for unknown call ID "+msg.call_id); console.log("Got candidates for unknown call ID "+msg.call_id);
return; return;
} }
call.gotRemoteIceCandidate(msg.candidate); for (var i = 0; i < msg.candidates.length; ++i) {
call.gotRemoteIceCandidate(msg.candidates[i]);
}
} else if (event.type == 'm.call.hangup') { } else if (event.type == 'm.call.hangup') {
var call = matrixPhoneService.allCalls[msg.call_id]; var call = matrixPhoneService.allCalls[msg.call_id];
if (!call) { if (!call) {
console.trace("Got hangup for unknown call ID "+msg.call_id); console.log("Got hangup for unknown call ID "+msg.call_id);
return; return;
} }
call.onHangupReceived(); call.onHangupReceived();
matrixPhoneService.allCalls[msg.call_id] = undefined; delete(matrixPhoneService.allCalls[msg.call_id]);
} }
}); });

View file

@ -235,6 +235,32 @@ angular.module('matrixService', [])
return doRequest("GET", path, undefined, {}); return doRequest("GET", path, undefined, {});
}, },
setName: function(room_id, name) {
var data = {
name: name
};
return this.sendStateEvent(room_id, "m.room.name", data);
},
setTopic: function(room_id, topic) {
var data = {
topic: topic
};
return this.sendStateEvent(room_id, "m.room.topic", data);
},
sendStateEvent: function(room_id, eventType, content, state_key) {
var path = "/rooms/$room_id/state/"+eventType;
if (state_key !== undefined) {
path += "/" + state_key;
}
room_id = encodeURIComponent(room_id);
path = path.replace("$room_id", room_id);
return doRequest("PUT", path, undefined, content);
},
sendEvent: function(room_id, eventType, txn_id, content) { sendEvent: function(room_id, eventType, txn_id, content) {
// The REST path spec // The REST path spec

View file

@ -142,4 +142,9 @@ angular.module('HomeController', ['matrixService', 'eventHandlerService', 'Recen
refresh(); refresh();
}; };
// Clean data when user logs out
$scope.$on(eventHandlerService.RESET_EVENT, function() {
$scope.public_rooms = [];
});
}]); }]);

View file

@ -24,11 +24,30 @@
<h3>Public rooms</h3> <h3>Public rooms</h3>
<div class="public_rooms" ng-repeat="room in public_rooms"> <table class="publicTable">
<div> <tbody ng-repeat="room in public_rooms | orderBy:'room_display_name'"
<a href="#/room/{{ room.room_alias ? room.room_alias : room.room_id }}" >{{ room.room_display_name }}</a> class="publicRoomEntry"
</div> ng-class="room.room_display_name.toLowerCase().indexOf('#matrix:') === 0 ? 'roomHighlight' : ''">
</div> <tr>
<td class="publicRoomEntry">
<a href="#/room/{{ room.room_alias ? room.room_alias : room.room_id }}" >
{{ room.room_display_name }}
</a>
</td>
<td>
<div class="publicRoomJoinedUsers"
ng-show="room.num_joined_members">
{{ room.num_joined_members }} {{ room.num_joined_members == 1 ? 'user' : 'users' }}
</div>
</td>
</tr>
<tr>
<td colspan="2" class="publicRoomTopic">
{{ room.topic }}
</td>
</tr>
</tbody>
</table>
<br/> <br/>
<div> <div>

View file

@ -15,7 +15,9 @@
<script src="js/angular.min.js"></script> <script src="js/angular.min.js"></script>
<script src="js/angular-route.min.js"></script> <script src="js/angular-route.min.js"></script>
<script src="js/angular-sanitize.min.js"></script> <script src="js/angular-sanitize.min.js"></script>
<script src="js/angular-animate.min.js"></script>
<script type='text/javascript' src='js/ng-infinite-scroll-matrix.js'></script> <script type='text/javascript' src='js/ng-infinite-scroll-matrix.js'></script>
<script type='text/javascript' src='js/autofill-event.js'></script>
<script src="app.js"></script> <script src="app.js"></script>
<script src="config.js"></script> <script src="config.js"></script>
<script src="app-controller.js"></script> <script src="app-controller.js"></script>
@ -56,9 +58,11 @@
<br /> <br />
<span id="callState"> <span id="callState">
<span ng-show="currentCall.state == 'invite_sent'">Calling...</span> <span ng-show="currentCall.state == 'invite_sent'">Calling...</span>
<span ng-show="currentCall.state == 'ringing'">Incoming Call</span>
<span ng-show="currentCall.state == 'connecting'">Call Connecting...</span> <span ng-show="currentCall.state == 'connecting'">Call Connecting...</span>
<span ng-show="currentCall.state == 'connected'">Call Connected</span> <span ng-show="currentCall.state == 'connected'">Call Connected</span>
<span ng-show="currentCall.state == 'ended' && !currentCall.didConnect && currentCall.direction == 'outbound'">Call Rejected</span> <span ng-show="currentCall.state == 'ended' && !currentCall.didConnect && currentCall.direction == 'outbound' && currentCall.hangupParty == 'remote'">Call Rejected</span>
<span ng-show="currentCall.state == 'ended' && !currentCall.didConnect && currentCall.direction == 'outbound' && currentCall.hangupParty == 'local'">Call Canceled</span>
<span ng-show="currentCall.state == 'ended' && currentCall.didConnect && currentCall.direction == 'outbound'">Call Ended</span> <span ng-show="currentCall.state == 'ended' && currentCall.didConnect && currentCall.direction == 'outbound'">Call Ended</span>
<span ng-show="currentCall.state == 'ended' && !currentCall.didConnect && currentCall.direction == 'inbound'">Call Canceled</span> <span ng-show="currentCall.state == 'ended' && !currentCall.didConnect && currentCall.direction == 'inbound'">Call Canceled</span>
<span ng-show="currentCall.state == 'ended' && currentCall.didConnect && currentCall.direction == 'inbound'">Call Ended</span> <span ng-show="currentCall.state == 'ended' && currentCall.didConnect && currentCall.direction == 'inbound'">Call Ended</span>

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,30 @@
/* /*
AngularJS v1.2.0 AngularJS v1.3.0-rc.1
(c) 2010-2012 Google, Inc. http://angularjs.org (c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(A,s,B){'use strict';s.module("ngAnimate",["ng"]).config(["$provide","$animateProvider",function(N,D){var z=s.noop,k=s.forEach,X=D.$$selectors,T=1,f="$$ngAnimateState",E="ng-animate",g={running:!0};N.decorator("$animate",["$delegate","$injector","$sniffer","$rootElement","$timeout","$rootScope","$document",function(t,A,F,m,G,p,H){function B(a){if(a){var d=[],b={};a=a.substr(1).split(".");(F.transitions||F.animations)&&a.push("");for(var c=0;c<a.length;c++){var h=a[c],f=X[h];f&&!b[h]&&(d.push(A.get(f)), (function(D,f,E){'use strict';f.module("ngAnimate",["ng"]).directive("ngAnimateChildren",function(){return function(P,w,g){g=g.ngAnimateChildren;f.isString(g)&&0===g.length?w.data("$$ngAnimateChildren",!0):P.$watch(g,function(f){w.data("$$ngAnimateChildren",!!f)})}}).factory("$$animateReflow",["$$rAF","$document",function(f,w){return function(g){return f(function(){g()})}}]).config(["$provide","$animateProvider",function(P,w){function g(f){for(var g=0;g<f.length;g++){var h=f[g];if(1==h.nodeType)return h}}
b[h]=!0)}return d}}function n(a,d,b,c,h,g,m){function p(a){g();if(!0===a)u();else{if(a=b.data(f))a.done=u,b.data(f,a);s(v,"after",u)}}function s(c,h,g){var f=h+"End";k(c,function(k,t){var e=function(){a:{var e=h+"Complete",a=c[t];a[e]=!0;(a[f]||z)();for(a=0;a<c.length;a++)if(!c[a][e])break a;g()}};"before"!=h||"enter"!=a&&"move"!=a?k[h]?k[f]=n?k[h](b,d,e):k[h](b,e):e():e()})}function t(){m&&G(m,0,!1)}function u(){if(!u.hasBeenRun){u.hasBeenRun=!0;var a=b.data(f);a&&(n?q(b):(a.closeAnimationTimeout= function Z(f,h){return g(f)==g(h)}var q=f.noop,h=f.forEach,$=w.$$selectors,W=f.isArray,u={running:!0};P.decorator("$animate",["$delegate","$$q","$injector","$sniffer","$rootElement","$$asyncCallback","$rootScope","$document","$templateRequest",function(Q,D,I,E,v,X,x,Y,J){function K(a,c){var d=a.data("$$ngAnimateState")||{};c&&(d.running=!0,d.structural=!0,a.data("$$ngAnimateState",d));return d.disabled||d.running&&d.structural}function L(a){var c,d=D.defer();d.promise.$$cancelFn=function(){c&&c()};
G(function(){q(b)},0,!1),b.data(f,a)));t()}}var r=(" "+((b.attr("class")||"")+" "+d)).replace(/\s+/g,".");c||(c=h?h.parent():b.parent());h=B(r);var n="addClass"==a||"removeClass"==a,r=b.data(f)||{};if(J(b,c)||0===h.length)g(),u();else{var v=[];r.running&&n&&r.structural||k(h,function(c){if(!c.allowCancel||c.allowCancel(b,a,d)){var h=c[a];"leave"==a?(c=h,h=null):c=c["before"+a.charAt(0).toUpperCase()+a.substr(1)];v.push({before:c,after:h})}});0===v.length?(g(),t()):(r.running&&(G.cancel(r.closeAnimationTimeout), x.$$postDigest(function(){c=a(function(){d.resolve()})});return d.promise}function M(a,c,d){d=d||{};var e={};h(c.add,function(b){b&&b.length&&(e[b]=e[b]||0,e[b]++)});h(c.remove,function(b){b&&b.length&&(e[b]=e[b]||0,e[b]--)});var R=[];h(d,function(b,a){h(a.split(" "),function(a){R[a]=b})});var g=[],l=[];h(e,function(b,c){var d=f.$$hasClass(a[0],c),e=R[c]||{};0>b?(d||"addClass"==e.event)&&l.push(c):0<b&&(d&&"removeClass"!=e.event||g.push(c))});return 0<g.length+l.length&&[g.join(" "),l.join(" ")]}
q(b),K(r.animations),(r.done||z)(!0)),"addClass"==a&&b.hasClass(d)||"removeClass"==a&&!b.hasClass(d)?(g(),t()):(b.addClass(E),b.data(f,{running:!0,structural:!n,animations:v,done:p}),s(v,"before",p)))}}function L(a){a=a[0];a.nodeType==T&&k(a.querySelectorAll("."+E),function(a){a=s.element(a);var b=a.data(f);b&&(K(b.animations),q(a))})}function K(a){k(a,function(d){a.beforeComplete||(d.beforeEnd||z)(!0);a.afterComplete||(d.afterEnd||z)(!0)})}function q(a){a[0]==m[0]?g.disabled||(g.running=!1,g.structural= function N(a){if(a){var c=[],d={};a=a.substr(1).split(".");(E.transitions||E.animations)&&c.push(I.get($[""]));for(var e=0;e<a.length;e++){var f=a[e],g=$[f];g&&!d[f]&&(c.push(I.get(g)),d[f]=!0)}return c}}function T(a,c,d){function e(b,a){var c=b[a],d=b["before"+a.charAt(0).toUpperCase()+a.substr(1)];if(c||d)return"leave"==a&&(d=c,c=null),U.push({event:a,fn:c}),m.push({event:a,fn:d}),!0}function f(c,r,e){var A=[];h(c,function(b){b.fn&&A.push(b)});var k=0;h(A,function(c,f){var g=function(){a:{if(r){(r[f]||
!1):(a.removeClass(E),a.removeData(f))}function J(a,d){if(g.disabled)return!0;if(a[0]==m[0])return g.disabled||g.running;do{if(0===d.length)break;var b=d[0]==m[0],c=b?g:d.data(f),c=c&&(!!c.disabled||!!c.running);if(b||c)return c;if(b)break}while(d=d.parent());return!0}m.data(f,g);p.$$postDigest(function(){g.running=!1});return{enter:function(a,d,b,c){this.enabled(!1,a);t.enter(a,d,b);p.$$postDigest(function(){n("enter","ng-enter",a,d,b,z,c)})},leave:function(a,d){L(a);this.enabled(!1,a);p.$$postDigest(function(){n("leave", q)();if(++k<A.length)break a;r=null}e()}};switch(c.event){case "setClass":r.push(c.fn(a,l,b,g));break;case "addClass":r.push(c.fn(a,l||d,g));break;case "removeClass":r.push(c.fn(a,b||d,g));break;default:r.push(c.fn(a,g))}});r&&0===r.length&&e()}var g=a[0];if(g){var l,b;W(d)&&(l=d[0],b=d[1],l?b?d=l+" "+b:(d=l,c="addClass"):(d=b,c="removeClass"));var r="setClass"==c,A=r||"addClass"==c||"removeClass"==c,y=a.attr("class")+" "+d;if(B(y)){var C=q,k=[],m=[],n=q,s=[],U=[],y=(" "+y).replace(/\s+/g,".");h(N(y),
"ng-leave",a,null,null,function(){t.leave(a)},d)})},move:function(a,d,b,c){L(a);this.enabled(!1,a);t.move(a,d,b);p.$$postDigest(function(){n("move","ng-move",a,d,b,z,c)})},addClass:function(a,d,b){n("addClass",d,a,null,null,function(){t.addClass(a,d)},b)},removeClass:function(a,d,b){n("removeClass",d,a,null,null,function(){t.removeClass(a,d)},b)},enabled:function(a,d){switch(arguments.length){case 2:if(a)q(d);else{var b=d.data(f)||{};b.disabled=!0;d.data(f,b)}break;case 1:g.disabled=!a;break;default:a= function(b){!e(b,c)&&r&&(e(b,"addClass"),e(b,"removeClass"))});return{node:g,event:c,className:d,isClassBased:A,isSetClassOperation:r,before:function(b){C=b;f(m,k,function(){C=q;b()})},after:function(b){n=b;f(U,s,function(){n=q;b()})},cancel:function(){k&&(h(k,function(b){(b||q)(!0)}),C(!0));s&&(h(s,function(b){(b||q)(!0)}),n(!0))}}}}}function F(a,c,d,e,g,S,l){function b(b){var l="$animate:"+b;m&&m[l]&&0<m[l].length&&X(function(){d.triggerHandler(l,{event:a,className:c})})}function r(){b("before")}
!g.disabled}return!!a}}}]);D.register("",["$window","$sniffer","$timeout",function(f,g,F){function m(e){Q.push(e);F.cancel(R);R=F(function(){k(Q,function(e){e()});Q=[];R=null;C={}},10,!1)}function G(e,a){var w=e.getAttribute("style")||"";e.setAttribute("style",(0<w.length?"; ":"")+a);return w}function p(e,a){var w=a?C[a]:null;if(!w){var b=0,d=0,c=0,l=0,g,n,m,p;k(e,function(e){if(e.nodeType==T){e=f.getComputedStyle(e)||{};m=e[h+D];b=Math.max(H(m),b);p=e[h+S];g=e[h+u];d=Math.max(H(g),d);n=e[I+u];l= function A(){b("after")}function y(){y.hasBeenRun||(y.hasBeenRun=!0,S())}function C(){if(!C.hasBeenRun){C.hasBeenRun=!0;var r=d.data("$$ngAnimateState");r&&(k&&k.isClassBased?z(d,c):(X(function(){var b=d.data("$$ngAnimateState")||{};p==b.index&&z(d,c,a)}),d.data("$$ngAnimateState",r)));b("close");l()}}var k=T(d,a,c);if(!k)return y(),r(),A(),C(),q;a=k.event;c=k.className;var m=f.element._data(k.node),m=m&&m.events;e||(e=g?g.parent():d.parent());if(H(d,e))return y(),r(),A(),C(),q;e=d.data("$$ngAnimateState")||
Math.max(H(n),l);var a=H(e[I+D]);0<a&&(a*=parseInt(e[I+r],10)||1);c=Math.max(a,c)}});w={total:0,transitionPropertyStyle:p,transitionDurationStyle:m,transitionDelayStyle:g,transitionDelay:d,transitionDuration:b,animationDelayStyle:n,animationDelay:l,animationDuration:c};a&&(C[a]=w)}return w}function H(e){var a=0;e=s.isString(e)?e.split(/\s*,\s*/):[];k(e,function(e){a=Math.max(parseFloat(e)||0,a)});return a}function E(e){var a=e.parent(),b=a.data(W);b||(a.data(W,++V),b=V);return b+"-"+e[0].className} {};var n=e.active||{},s=e.totalActive||0,U=e.last;g=!1;if(0<s){s=[];if(k.isClassBased)"setClass"==U.event?(s.push(U),z(d,c)):n[c]&&(t=n[c],t.event==a?g=!0:(s.push(t),z(d,c)));else if("leave"==a&&n["ng-leave"])g=!0;else{for(var t in n)s.push(n[t]);e={};z(d,!0)}0<s.length&&h(s,function(b){b.cancel()})}!k.isClassBased||k.isSetClassOperation||g||(g="addClass"==a==d.hasClass(c));if(g)return y(),r(),A(),b("close"),l(),q;n=e.active||{};s=e.totalActive||0;if("leave"==a)d.one("$destroy",function(b){b=f.element(this);
function n(e,a){var b=E(e),d=b+" "+a,c={},g=C[d]?++C[d].total:0;if(0<g){var l=a+"-stagger",c=b+" "+l;(b=!C[c])&&e.addClass(l);c=p(e,c);b&&e.removeClass(l)}e.addClass(a);d=p(e,d);l=Math.max(d.transitionDuration,d.animationDuration);if(0===l)return e.removeClass(a),!1;var b=e[0],f="";0<d.transitionDuration&&(e.addClass(U),f+=N+" ",b.style[h+S]="none");k(a.split(" "),function(a,e){f+=(0<e?" ":"")+a+"-active"});e.data(v,{className:a,activeClassName:f,maxDuration:l,classes:a+" "+f,timings:d,stagger:c, var a=b.data("$$ngAnimateState");a&&(a=a.active["ng-leave"])&&(a.cancel(),z(b,"ng-leave"))});d.addClass("ng-animate");var p=G++;s++;n[c]=k;d.data("$$ngAnimateState",{last:k,active:n,index:p,totalActive:s});r();k.before(function(b){var l=d.data("$$ngAnimateState");b=b||!l||!l.active[c]||k.isClassBased&&l.active[c].event!=a;y();!0===b?C():(A(),k.after(C))});return k.cancel}function p(a){if(a=g(a))a=f.isFunction(a.getElementsByClassName)?a.getElementsByClassName("ng-animate"):a.querySelectorAll(".ng-animate"),
ii:g});return!0}function L(a,b,w){function f(a){a.stopPropagation();a=a.originalEvent||a;var e=a.$manualTimeStamp||a.timeStamp||Date.now();Math.max(e-t,0)>=p&&a.elapsedTime>=n&&w()}var x=a.data(v);if(a.hasClass(b)&&x){var M=a[0],l=x.timings,k=x.stagger,n=x.maxDuration,m=x.activeClassName,p=1E3*Math.max(l.transitionDelay,l.animationDelay),t=Date.now(),s=P+" "+O,r,x=x.ii,u,y="";if(0<l.transitionDuration){M.style[h+S]="";var q=l.transitionPropertyStyle;-1==q.indexOf("all")&&(u=!0,y+=c+"transition-property: "+ h(a,function(a){a=f.element(a);(a=a.data("$$ngAnimateState"))&&a.active&&h(a.active,function(a){a.cancel()})})}function z(a,c){if(Z(a,v))u.disabled||(u.running=!1,u.structural=!1);else if(c){var d=a.data("$$ngAnimateState")||{},e=!0===c;!e&&d.active&&d.active[c]&&(d.totalActive--,delete d.active[c]);if(e||!d.totalActive)a.removeClass("ng-animate"),a.removeData("$$ngAnimateState")}}function H(a,c){if(u.disabled)return!0;if(Z(a,v))return u.running;var d,e,g;do{if(0===c.length)break;var h=Z(c,v),l=h?
q+", "+(g.msie?"-ms-zoom":"clip")+"; ",y+=c+"transition-duration: "+l.transitionDurationStyle+", "+l.transitionDuration+"s; ")}0<x&&(0<k.transitionDelay&&0===k.transitionDuration&&(q=l.transitionDelayStyle,u&&(q+=", "+l.transitionDelay+"s"),y+=c+"transition-delay: "+K(q,k.transitionDelay,x)+"; "),0<k.animationDelay&&0===k.animationDuration&&(y+=c+"animation-delay: "+K(l.animationDelayStyle,k.animationDelay,x)+"; "));0<y.length&&(r=G(M,y));a.on(s,f);a.addClass(m);return function(c){a.off(s,f);a.removeClass(m); u:c.data("$$ngAnimateState")||{};if(l.disabled)return!0;h&&(g=!0);!1!==d&&(h=c.data("$$ngAnimateChildren"),f.isDefined(h)&&(d=h));e=e||l.running||l.last&&!l.last.isClassBased}while(c=c.parent());return!g||!d&&e}v.data("$$ngAnimateState",u);var V=x.$watch(function(){return J.totalPendingRequests},function(a,c){0===a&&(V(),x.$$postDigest(function(){x.$$postDigest(function(){u.running=!1})}))}),G=0,O=w.classNameFilter(),B=O?function(a){return O.test(a)}:function(){return!0};return{enter:function(a,c,
d(a,b);null!=r&&(0<r.length?M.setAttribute("style",r):M.removeAttribute("style"))}}w()}function K(a,b,d){var c="";k(a.split(","),function(a,e){c+=(0<e?",":"")+(d*b+parseInt(a,10))+"s"});return c}function q(a,b){if(n(a,b))return function(c){c&&d(a,b)}}function J(a,b,c){if(a.data(v))return L(a,b,c);d(a,b);c()}function a(a,b,c){var d=q(a,b);if(d){var f=d;m(function(){f=J(a,b,c)});return function(a){(f||z)(a)}}c()}function d(a,b){a.removeClass(b);a.removeClass(U);a.removeData(v)}function b(a,b){var c= d){a=f.element(a);c=c&&f.element(c);d=d&&f.element(d);K(a,!0);Q.enter(a,c,d);return L(function(e){return F("enter","ng-enter",f.element(g(a)),c,d,q,e)})},leave:function(a){a=f.element(a);p(a);K(a,!0);this.enabled(!1,a);return L(function(c){return F("leave","ng-leave",f.element(g(a)),null,null,function(){Q.leave(a)},c)})},move:function(a,c,d){a=f.element(a);c=c&&f.element(c);d=d&&f.element(d);p(a);K(a,!0);Q.move(a,c,d);return L(function(e){return F("move","ng-move",f.element(g(a)),c,d,q,e)})},addClass:function(a,
"";a=s.isArray(a)?a:a.split(/\s+/);k(a,function(a,e){a&&0<a.length&&(c+=(0<e?" ":"")+a+b)});return c}var c="",h,O,I,P;A.ontransitionend===B&&A.onwebkittransitionend!==B?(c="-webkit-",h="WebkitTransition",O="webkitTransitionEnd transitionend"):(h="transition",O="transitionend");A.onanimationend===B&&A.onwebkitanimationend!==B?(c="-webkit-",I="WebkitAnimation",P="webkitAnimationEnd animationend"):(I="animation",P="animationend");var D="Duration",S="Property",u="Delay",r="IterationCount",W="$$ngAnimateKey", c){return this.setClass(a,c,[])},removeClass:function(a,c){return this.setClass(a,[],c)},setClass:function(a,c,d){a=f.element(a);a=f.element(g(a));if(K(a))return Q.setClass(a,c,d);c=W(c)?c:c.split(" ");d=W(d)?d:d.split(" ");var e=a.data("$$animateClasses");if(e)return e.add=e.add.concat(c),e.remove=e.remove.concat(d),e.promise;a.data("$$animateClasses",e={add:c,remove:d});return e.promise=L(function(c){var d=a.data("$$animateClasses");a.removeData("$$animateClasses");var l=a.data("$$ngAnimateState")||
v="$$ngAnimateCSS3Data",U="ng-animate-start",N="ng-animate-active",C={},V=0,Q=[],R;return{allowCancel:function(a,c,d){var f=(a.data(v)||{}).classes;if(!f||0<=["enter","leave","move"].indexOf(c))return!0;var h=a.parent(),g=s.element(a[0].cloneNode());g.attr("style","position:absolute; top:-9999px; left:-9999px");g.removeAttr("id");g.html("");k(f.split(" "),function(a){g.removeClass(a)});g.addClass(b(d,"addClass"==c?"-add":"-remove"));h.append(g);a=p(g);g.remove();return 0<Math.max(a.transitionDuration, {},b=M(a,d,l.active);return b?F("setClass",b,a,null,null,function(){Q.setClass(a,b[0],b[1])},c):c()})},cancel:function(a){a.$$cancelFn()},enabled:function(a,c){switch(arguments.length){case 2:if(a)z(c);else{var d=c.data("$$ngAnimateState")||{};d.disabled=!0;c.data("$$ngAnimateState",d)}break;case 1:u.disabled=!a;break;default:a=!u.disabled}return!!a}}}]);w.register("",["$window","$sniffer","$timeout","$$animateReflow",function(u,w,I,P){function v(a,b){d&&d();c.push(b);d=P(function(){h(c,function(b){b()});
a.animationDuration)},enter:function(b,c){return a(b,"ng-enter",c)},leave:function(b,c){return a(b,"ng-leave",c)},move:function(b,c){return a(b,"ng-move",c)},beforeAddClass:function(a,c,d){if(a=q(a,b(c,"-add")))return m(d),a;d()},addClass:function(a,c,d){return J(a,b(c,"-add"),d)},beforeRemoveClass:function(a,c,d){if(a=q(a,b(c,"-remove")))return m(d),a;d()},removeClass:function(a,c,d){return J(a,b(c,"-remove"),d)}}}])}])})(window,window.angular); c=[];d=null;B={}})}function X(a,b){var c=g(a);a=f.element(c);S.push(a);c=Date.now()+b;c<=R||(I.cancel(e),R=c,e=I(function(){x(S);S=[]},b,!1))}function x(a){h(a,function(b){(b=b.data("$$ngAnimateCSS3Data"))&&h(b.closeAnimationFns,function(b){b()})})}function Y(a,b){var c=b?B[b]:null;if(!c){var d=0,e=0,g=0,f=0;h(a,function(b){if(1==b.nodeType){b=u.getComputedStyle(b)||{};d=Math.max(J(b[H+"Duration"]),d);e=Math.max(J(b[H+"Delay"]),e);f=Math.max(J(b[G+"Delay"]),f);var a=J(b[G+"Duration"]);0<a&&(a*=parseInt(b[G+
"IterationCount"],10)||1);g=Math.max(a,g)}});c={total:0,transitionDelay:e,transitionDuration:d,animationDelay:f,animationDuration:g};b&&(B[b]=c)}return c}function J(a){var b=0;a=f.isString(a)?a.split(/\s*,\s*/):[];h(a,function(a){b=Math.max(parseFloat(a)||0,b)});return b}function K(c,b,d){c=0<=["ng-enter","ng-leave","ng-move"].indexOf(d);var e,f=b.parent(),h=f.data("$$ngAnimateKey");h||(f.data("$$ngAnimateKey",++a),h=a);e=h+"-"+g(b).getAttribute("class");var f=e+" "+d,h=B[f]?++B[f].total:0,k={};if(0<
h){var m=d+"-stagger",k=e+" "+m;(e=!B[k])&&b.addClass(m);k=Y(b,k);e&&b.removeClass(m)}b.addClass(d);var m=b.data("$$ngAnimateCSS3Data")||{},n=Y(b,f);e=n.transitionDuration;n=n.animationDuration;if(c&&0===e&&0===n)return b.removeClass(d),!1;d=c&&0<e;c=0<n&&0<k.animationDelay&&0===k.animationDuration;b.data("$$ngAnimateCSS3Data",{stagger:k,cacheKey:f,running:m.running||0,itemIndex:h,blockTransition:d,closeAnimationFns:m.closeAnimationFns||[]});b=g(b);d&&(b.style[H+"Property"]="none");c&&(b.style[G+
"PlayState"]="paused");return!0}function L(a,b,c,d){function e(a){b.off(D,f);b.removeClass(m);b.removeClass(n);x&&I.cancel(x);F(b,c);a=g(b);for(var d in s)a.style.removeProperty(s[d])}function f(b){b.stopPropagation();var a=b.originalEvent||b;b=a.$manualTimeStamp||a.timeStamp||Date.now();a=parseFloat(a.elapsedTime.toFixed(3));Math.max(b-E,0)>=B&&a>=w&&d()}var k=g(b);a=b.data("$$ngAnimateCSS3Data");if(-1!=k.getAttribute("class").indexOf(c)&&a){a.blockTransition&&(k.style[H+"Property"]="");var m="",
n="";h(c.split(" "),function(b,a){var c=(0<a?" ":"")+b;m+=c+"-active";n+=c+"-pending"});var s=[],p=a.itemIndex,t=a.stagger,q=0;if(0<p){q=0;0<t.transitionDelay&&0===t.transitionDuration&&(q=t.transitionDelay*p);var u=0;0<t.animationDelay&&0===t.animationDuration&&(u=t.animationDelay*p,s.push(z+"animation-play-state"));q=Math.round(100*Math.max(q,u))/100}q||b.addClass(m);var v=Y(b,a.cacheKey+" "+m),w=Math.max(v.transitionDuration,v.animationDuration);if(0===w)b.removeClass(m),F(b,c),d();else{var p=
Math.max(v.transitionDelay,v.animationDelay),B=1E3*p;0<s.length&&(t=k.getAttribute("style")||"",";"!==t.charAt(t.length-1)&&(t+=";"),k.setAttribute("style",t+" "));var E=Date.now(),D=O+" "+V,p=1E3*(q+1.5*(p+w)),x;0<q&&(b.addClass(n),x=I(function(){x=null;b.addClass(m);b.removeClass(n);0<v.animationDuration&&(k.style[G+"PlayState"]="")},1E3*q,!1));b.on(D,f);a.closeAnimationFns.push(function(){e();d()});a.running++;X(b,p);return e}}else d()}function M(a,b,c,d){if(K(a,b,c,d))return function(a){a&&F(b,
c)}}function N(a,b,c,d){if(b.data("$$ngAnimateCSS3Data"))return L(a,b,c,d);F(b,c);d()}function T(a,b,c,d){var e=M(a,b,c);if(e){var f=e;v(b,function(){f=N(a,b,c,d)});return function(b){(f||q)(b)}}d()}function F(a,b){a.removeClass(b);var c=a.data("$$ngAnimateCSS3Data");c&&(c.running&&c.running--,c.running&&0!==c.running||a.removeData("$$ngAnimateCSS3Data"))}function p(a,b){var c="";a=W(a)?a:a.split(/\s+/);h(a,function(a,d){a&&0<a.length&&(c+=(0<d?" ":"")+a+b)});return c}var z="",H,V,G,O;D.ontransitionend===
E&&D.onwebkittransitionend!==E?(z="-webkit-",H="WebkitTransition",V="webkitTransitionEnd transitionend"):(H="transition",V="transitionend");D.onanimationend===E&&D.onwebkitanimationend!==E?(z="-webkit-",G="WebkitAnimation",O="webkitAnimationEnd animationend"):(G="animation",O="animationend");var B={},a=0,c=[],d,e=null,R=0,S=[];return{enter:function(a,b){return T("enter",a,"ng-enter",b)},leave:function(a,b){return T("leave",a,"ng-leave",b)},move:function(a,b){return T("move",a,"ng-move",b)},beforeSetClass:function(a,
b,c,d){b=p(c,"-remove")+" "+p(b,"-add");if(b=M("setClass",a,b))return v(a,d),b;d()},beforeAddClass:function(a,b,c){if(b=M("addClass",a,p(b,"-add")))return v(a,c),b;c()},beforeRemoveClass:function(a,b,c){if(b=M("removeClass",a,p(b,"-remove")))return v(a,c),b;c()},setClass:function(a,b,c,d){c=p(c,"-remove");b=p(b,"-add");return N("setClass",a,c+" "+b,d)},addClass:function(a,b,c){return N("addClass",a,p(b,"-add"),c)},removeClass:function(a,b,c){return N("removeClass",a,p(b,"-remove"),c)}}}])}])})(window,
window.angular);
//# sourceMappingURL=angular-animate.min.js.map //# sourceMappingURL=angular-animate.min.js.map

View file

@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.20 * @license AngularJS v1.3.0-rc.1
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@ -22,12 +22,12 @@
*/ */
/* global -ngRouteModule */ /* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']). var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider); provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/** /**
* @ngdoc provider * @ngdoc provider
* @name $routeProvider * @name $routeProvider
* @kind function
* *
* @description * @description
* *
@ -216,10 +216,14 @@ function $RouteProvider(){
* Sets route definition that will be used on route change when no other route definition * Sets route definition that will be used on route change when no other route definition
* is matched. * is matched.
* *
* @param {Object} params Mapping information to be assigned to `$route.current`. * @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self * @returns {Object} self
*/ */
this.otherwise = function(params) { this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params); this.when(null, params);
return this; return this;
}; };
@ -230,10 +234,9 @@ function $RouteProvider(){
'$routeParams', '$routeParams',
'$q', '$q',
'$injector', '$injector',
'$http', '$templateRequest',
'$templateCache',
'$sce', '$sce',
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) { function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/** /**
* @ngdoc service * @ngdoc service
@ -441,6 +444,36 @@ function $RouteProvider(){
reload: function() { reload: function() {
forceReload = true; forceReload = true;
$rootScope.$evalAsync(updateRoute); $rootScope.$evalAsync(updateRoute);
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {Object} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
var searchParams = {}, self=this;
angular.forEach(Object.keys(newParams), function(key) {
if (!self.current.pathParams[key]) searchParams[key] = newParams[key];
});
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
$location.search(angular.extend({}, $location.search(), searchParams));
}
else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
} }
}; };
@ -473,9 +506,7 @@ function $RouteProvider(){
for (var i = 1, len = m.length; i < len; ++i) { for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1]; var key = keys[i - 1];
var val = 'string' == typeof m[i] var val = m[i];
? decodeURIComponent(m[i])
: m[i];
if (key && val) { if (key && val) {
params[key.name] = val; params[key.name] = val;
@ -518,7 +549,7 @@ function $RouteProvider(){
angular.forEach(locals, function(value, key) { angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ? locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value); $injector.get(value) : $injector.invoke(value, null, null, key);
}); });
if (angular.isDefined(template = next.template)) { if (angular.isDefined(template = next.template)) {
@ -532,8 +563,7 @@ function $RouteProvider(){
templateUrl = $sce.getTrustedResourceUrl(templateUrl); templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) { if (angular.isDefined(templateUrl)) {
next.loadedTemplateUrl = templateUrl; next.loadedTemplateUrl = templateUrl;
template = $http.get(templateUrl, {cache: $templateCache}). template = $templateRequest(templateUrl);
then(function(response) { return response.data; });
} }
} }
if (angular.isDefined(template)) { if (angular.isDefined(template)) {
@ -695,7 +725,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
<pre>$location.path() = {{main.$location.path()}}</pre> <pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre> <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre> <pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre> <pre>$routeParams = {{main.$routeParams}}</pre>
</div> </div>
</file> </file>
@ -846,7 +875,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
currentScope = null; currentScope = null;
} }
if(currentElement) { if(currentElement) {
$animate.leave(currentElement, function() { $animate.leave(currentElement).then(function() {
previousElement = null; previousElement = null;
}); });
previousElement = currentElement; previousElement = currentElement;
@ -869,7 +898,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
// function is called before linking the content, which would apply child // function is called before linking the content, which would apply child
// directives to non existing elements. // directives to non existing elements.
var clone = $transclude(newScope, function(clone) { var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter () {
if (angular.isDefined(autoScrollExp) if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) { && (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll(); $anchorScroll();

View file

@ -1,14 +1,14 @@
/* /*
AngularJS v1.2.0 AngularJS v1.3.0-rc.1
(c) 2010-2012 Google, Inc. http://angularjs.org (c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(t,c,B){'use strict';function w(s,r,g,a,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(k,d,A){return function(u,k,d){function v(){l&&(l.$destroy(),l=null);m&&(h.leave(m),m=null)}function x(){var f=s.current&&s.current.locals,y=f&&f.$template;if(y){var z=u.$new();A(z,function(e){e.html(y);h.enter(e,null,m||k,function(){!c.isDefined(n)||n&&!u.$eval(n)||r()});v();var p=g(e.contents()),q=s.current;l=q.scope=z;m=e;if(q.controller){f.$scope=l;var d=a(q.controller, (function(r,d,z){'use strict';function v(s,h,f){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,e,b,g,u){function w(){k&&(k.remove(),k=null);l&&(l.$destroy(),l=null);n&&(f.leave(n).then(function(){k=null}),k=n,n=null)}function t(){var c=s.current&&s.current.locals;if(d.isDefined(c&&c.$template)){var c=a.$new(),m=s.current;n=u(c,function(c){f.enter(c,null,n||e).then(function(){!d.isDefined(p)||p&&!a.$eval(p)||h()});w()});l=m.scope=c;l.$emit("$viewContentLoaded");
f);q.controllerAs&&(l[q.controllerAs]=d);e.data("$ngControllerController",d);e.children().data("$ngControllerController",d)}p(l);l.$emit("$viewContentLoaded");l.$eval(b)})}else v()}var l,m,n=d.autoscroll,b=d.onload||"";u.$on("$routeChangeSuccess",x);x()}}}}t=c.module("ngRoute",["ng"]).provider("$route",function(){function s(a,h){return c.extend(new (c.extend(function(){},{prototype:a})),h)}function r(a,c){var k=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g, l.$eval(q)}else w()}var l,n,k,p=b.autoscroll,q=b.onload||"";a.$on("$routeChangeSuccess",t);t()}}}function x(d,h,f){return{restrict:"ECA",priority:-400,link:function(a,e){var b=f.current,g=b.locals;e.html(g.$template);var u=d(e.contents());b.controller&&(g.$scope=a,g=h(b.controller,g),b.controllerAs&&(a[b.controllerAs]=g),e.data("$ngControllerController",g),e.children().data("$ngControllerController",g));u(a)}}}r=d.module("ngRoute",["ng"]).provider("$route",function(){function s(a,e){return d.extend(new (d.extend(function(){},
"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(a,c,h,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:h,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=RegExp("^"+a+"$",k?"i":"");return d}var g={};this.when=function(a,h){g[a]=c.extend({reloadOnSearch:!0},h,a&&r(a,h));if(a){var k="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[k]=c.extend({redirectTo:a},r(k,h))}return this};this.otherwise=function(a){this.when(null, {prototype:a})),e)}function h(a,d){var b=d.caseInsensitiveMatch,g={originalPath:a,regexp:a},f=g.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,d,e,b){a="?"===b?b:null;b="*"===b?b:null;f.push({name:e,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(b&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");g.regexp=new RegExp("^"+a+"$",b?"i":"");return g}var f={};this.when=function(a,e){f[a]=d.extend({reloadOnSearch:!0},e,a&&h(a,e));if(a){var b=
a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,h,k,d,r,u,t,w){function v(){var b=x(),f=n.current;if(b&&f&&b.$$route===f.$$route&&c.equals(b.pathParams,f.pathParams)&&!b.reloadOnSearch&&!m)f.params=b.params,c.copy(f.params,k),a.$broadcast("$routeUpdate",f);else if(b||f)m=!1,a.$broadcast("$routeChangeStart",b,f),(n.current=b)&&b.redirectTo&&(c.isString(b.redirectTo)?h.path(l(b.redirectTo,b.params)).search(b.params).replace(): "/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";f[b]=d.extend({redirectTo:a},h(b,e))}return this};this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,e,b,g,h,r,t){function l(){var c=n(),m=q.current;if(c&&m&&c.$$route===m.$$route&&d.equals(c.pathParams,m.pathParams)&&!c.reloadOnSearch&&!p)m.params=c.params,d.copy(m.params,b),a.$broadcast("$routeUpdate",
h.url(b.redirectTo(b.pathParams,h.path(),h.search())).replace()),d.when(b).then(function(){if(b){var a=c.extend({},b.resolve),f,e;c.forEach(a,function(b,f){a[f]=c.isString(b)?r.get(b):r.invoke(b)});c.isDefined(f=b.template)?c.isFunction(f)&&(f=f(b.params)):c.isDefined(e=b.templateUrl)&&(c.isFunction(e)&&(e=e(b.params)),e=w.getTrustedResourceUrl(e),c.isDefined(e)&&(b.loadedTemplateUrl=e,f=u.get(e,{cache:t}).then(function(b){return b.data})));c.isDefined(f)&&(a.$template=f);return d.all(a)}}).then(function(d){b== m);else if(c||m)p=!1,a.$broadcast("$routeChangeStart",c,m),(q.current=c)&&c.redirectTo&&(d.isString(c.redirectTo)?e.path(k(c.redirectTo,c.params)).search(c.params).replace():e.url(c.redirectTo(c.pathParams,e.path(),e.search())).replace()),g.when(c).then(function(){if(c){var a=d.extend({},c.resolve),e,b;d.forEach(a,function(c,b){a[b]=d.isString(c)?h.get(c):h.invoke(c,null,null,b)});d.isDefined(e=c.template)?d.isFunction(e)&&(e=e(c.params)):d.isDefined(b=c.templateUrl)&&(d.isFunction(b)&&(b=b(c.params)),
n.current&&(b&&(b.locals=d,c.copy(b.params,k)),a.$broadcast("$routeChangeSuccess",b,f))},function(c){b==n.current&&a.$broadcast("$routeChangeError",b,f,c)})}function x(){var b,a;c.forEach(g,function(d,l){var e;if(e=!a){var p=h.path();e=d.keys;var q={};if(d.regexp)if(p=d.regexp.exec(p)){for(var g=1,k=p.length;g<k;++g){var m=e[g-1],n="string"==typeof p[g]?decodeURIComponent(p[g]):p[g];m&&n&&(q[m.name]=n)}e=q}else e=null;else e=null;e=b=e}e&&(a=s(d,{params:c.extend({},h.search(),b),pathParams:b}),a.$$route= b=t.getTrustedResourceUrl(b),d.isDefined(b)&&(c.loadedTemplateUrl=b,e=r(b)));d.isDefined(e)&&(a.$template=e);return g.all(a)}}).then(function(e){c==q.current&&(c&&(c.locals=e,d.copy(c.params,b)),a.$broadcast("$routeChangeSuccess",c,m))},function(d){c==q.current&&a.$broadcast("$routeChangeError",c,m,d)})}function n(){var c,a;d.forEach(f,function(b,g){var f;if(f=!a){var h=e.path();f=b.keys;var l={};if(b.regexp)if(h=b.regexp.exec(h)){for(var k=1,n=h.length;k<n;++k){var p=f[k-1],q=h[k];p&&q&&(l[p.name]=
d)});return a||g[null]&&s(g[null],{params:{},pathParams:{}})}function l(a,d){var g=[];c.forEach((a||"").split(":"),function(a,b){if(0===b)g.push(a);else{var c=a.match(/(\w+)(.*)/),h=c[1];g.push(d[h]);g.push(c[2]||"");delete d[h]}});return g.join("")}var m=!1,n={routes:g,reload:function(){m=!0;a.$evalAsync(v)}};a.$on("$locationChangeSuccess",v);return n}]});t.provider("$routeParams",function(){this.$get=function(){return{}}});t.directive("ngView",w);w.$inject=["$route","$anchorScroll","$compile","$controller", q)}f=l}else f=null;else f=null;f=c=f}f&&(a=s(b,{params:d.extend({},e.search(),c),pathParams:c}),a.$$route=b)});return a||f[null]&&s(f[null],{params:{},pathParams:{}})}function k(a,b){var e=[];d.forEach((a||"").split(":"),function(a,c){if(0===c)e.push(a);else{var d=a.match(/(\w+)(.*)/),f=d[1];e.push(b[f]);e.push(d[2]||"");delete b[f]}});return e.join("")}var p=!1,q={routes:f,reload:function(){p=!0;a.$evalAsync(l)},updateParams:function(a){if(this.current&&this.current.$$route){var b={},f=this;d.forEach(Object.keys(a),
"$animate"]})(window,window.angular); function(d){f.current.pathParams[d]||(b[d]=a[d])});a=d.extend({},this.current.params,a);e.path(k(this.current.$$route.originalPath,a));e.search(d.extend({},e.search(),b))}else throw y("norout");}};a.$on("$locationChangeSuccess",l);return q}]});var y=d.$$minErr("ngRoute");r.provider("$routeParams",function(){this.$get=function(){return{}}});r.directive("ngView",v);r.directive("ngView",x);v.$inject=["$route","$anchorScroll","$animate"];x.$inject=["$compile","$controller","$route"]})(window,window.angular);
//# sourceMappingURL=angular-route.min.js.map //# sourceMappingURL=angular-route.min.js.map

View file

@ -1,6 +1,6 @@
/** /**
* @license AngularJS v1.2.0 * @license AngularJS v1.3.0-rc.1
* (c) 2010-2012 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
(function(window, angular, undefined) {'use strict'; (function(window, angular, undefined) {'use strict';
@ -8,7 +8,7 @@
var $sanitizeMinErr = angular.$$minErr('$sanitize'); var $sanitizeMinErr = angular.$$minErr('$sanitize');
/** /**
* @ngdoc overview * @ngdoc module
* @name ngSanitize * @name ngSanitize
* @description * @description
* *
@ -16,7 +16,6 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
* *
* The `ngSanitize` module provides functionality to sanitize HTML. * The `ngSanitize` module provides functionality to sanitize HTML.
* *
* {@installModule sanitize}
* *
* <div doc-module-components="ngSanitize"></div> * <div doc-module-components="ngSanitize"></div>
* *
@ -42,8 +41,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
/** /**
* @ngdoc service * @ngdoc service
* @name ngSanitize.$sanitize * @name $sanitize
* @function * @kind function
* *
* @description * @description
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
@ -51,25 +50,28 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
* it into the returned string, however, since our parser is more strict than a typical browser * it into the returned string, however, since our parser is more strict than a typical browser
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
* browser, won't make it through the sanitizer. * browser, won't make it through the sanitizer.
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
* *
* @param {string} html Html input. * @param {string} html Html input.
* @returns {string} Sanitized html. * @returns {string} Sanitized html.
* *
* @example * @example
<doc:example module="ngSanitize"> <example module="sanitizeExample" deps="angular-sanitize.js">
<doc:source> <file name="index.html">
<script> <script>
function Ctrl($scope, $sce) { angular.module('sanitizeExample', ['ngSanitize'])
$scope.snippet = .controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
'<p style="color:blue">an html\n' + $scope.snippet =
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' + '<p style="color:blue">an html\n' +
'snippet</p>'; '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
$scope.deliberatelyTrustDangerousSnippet = function() { 'snippet</p>';
return $sce.trustAsHtml($scope.snippet); $scope.deliberatelyTrustDangerousSnippet = function() {
}; return $sce.trustAsHtml($scope.snippet);
} };
}]);
</script> </script>
<div ng-controller="Ctrl"> <div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table> <table>
<tr> <tr>
@ -101,56 +103,71 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
</tr> </tr>
</table> </table>
</div> </div>
</doc:source> </file>
<doc:scenario> <file name="protractor.js" type="protractor">
it('should sanitize the html snippet by default', function() { it('should sanitize the html snippet by default', function() {
expect(using('#bind-html-with-sanitize').element('div').html()). expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
}); });
it('should inline raw snippet if bound to a trusted value', function() { it('should inline raw snippet if bound to a trusted value', function() {
expect(using('#bind-html-with-trust').element("div").html()). expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
toBe("<p style=\"color:blue\">an html\n" + toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>"); "snippet</p>");
}); });
it('should escape snippet without any filter', function() { it('should escape snippet without any filter', function() {
expect(using('#bind-default').element('div').html()). expect(element(by.css('#bind-default div')).getInnerHtml()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" + toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" + "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;"); "snippet&lt;/p&gt;");
}); });
it('should update', function() { it('should update', function() {
input('snippet').enter('new <b onclick="alert(1)">text</b>'); element(by.model('snippet')).clear();
expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>'); element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
expect(using('#bind-html-with-trust').element('div').html()).toBe( expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('new <b>text</b>');
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
'new <b onclick="alert(1)">text</b>'); 'new <b onclick="alert(1)">text</b>');
expect(using('#bind-default').element('div').html()).toBe( expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;"); "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
}); });
</doc:scenario> </file>
</doc:example> </example>
*/ */
var $sanitize = function(html) { function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
return function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
}));
return buf.join('');
};
}];
}
function sanitizeText(chars) {
var buf = []; var buf = [];
htmlParser(html, htmlSanitizeWriter(buf)); var writer = htmlSanitizeWriter(buf, angular.noop);
return buf.join(''); writer.chars(chars);
}; return buf.join('');
}
// Regular Expressions for parsing tags and attributes // Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP = var START_TAG_REGEXP =
/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/, /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/, END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
BEGIN_TAG_REGEXP = /^</, BEGIN_TAG_REGEXP = /^</,
BEGING_END_TAGE_REGEXP = /^<\s*\//, BEGING_END_TAGE_REGEXP = /^<\//,
COMMENT_REGEXP = /<!--(.*?)-->/g, COMMENT_REGEXP = /<!--(.*?)-->/g,
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i, DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i, SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character) // Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
@ -197,7 +214,7 @@ var validAttrs = angular.extend({}, uriAttrs, makeMap(
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+ 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+ 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+ 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
'scope,scrolling,shape,span,start,summary,target,title,type,'+ 'scope,scrolling,shape,size,span,start,summary,target,title,type,'+
'valign,value,vspace,width')); 'valign,value,vspace,width'));
function makeMap(str) { function makeMap(str) {
@ -220,10 +237,18 @@ function makeMap(str) {
* @param {object} handler * @param {object} handler
*/ */
function htmlParser( html, handler ) { function htmlParser( html, handler ) {
var index, chars, match, stack = [], last = html; if (typeof html !== 'string') {
if (html === null || typeof html === 'undefined') {
html = '';
} else {
html = '' + html;
}
}
var index, chars, match, stack = [], last = html, text;
stack.last = function() { return stack[ stack.length - 1 ]; }; stack.last = function() { return stack[ stack.length - 1 ]; };
while ( html ) { while ( html ) {
text = '';
chars = true; chars = true;
// Make sure we're not in a script or style element // Make sure we're not in a script or style element
@ -244,7 +269,7 @@ function htmlParser( html, handler ) {
match = html.match( DOCTYPE_REGEXP ); match = html.match( DOCTYPE_REGEXP );
if ( match ) { if ( match ) {
html = html.replace( match[0] , ''); html = html.replace( match[0], '');
chars = false; chars = false;
} }
// end tag // end tag
@ -262,16 +287,23 @@ function htmlParser( html, handler ) {
match = html.match( START_TAG_REGEXP ); match = html.match( START_TAG_REGEXP );
if ( match ) { if ( match ) {
html = html.substring( match[0].length ); // We only have a valid start-tag if there is a '>'.
match[0].replace( START_TAG_REGEXP, parseStartTag ); if ( match[4] ) {
html = html.substring( match[0].length );
match[0].replace( START_TAG_REGEXP, parseStartTag );
}
chars = false; chars = false;
} else {
// no ending tag found --- this piece should be encoded as an entity.
text += '<';
html = html.substring(1);
} }
} }
if ( chars ) { if ( chars ) {
index = html.indexOf("<"); index = html.indexOf("<");
var text = index < 0 ? html : html.substring( 0, index ); text += index < 0 ? html : html.substring( 0, index );
html = index < 0 ? "" : html.substring( index ); html = index < 0 ? "" : html.substring( index );
if (handler.chars) handler.chars( decodeEntities(text) ); if (handler.chars) handler.chars( decodeEntities(text) );
@ -351,15 +383,32 @@ function htmlParser( html, handler ) {
} }
} }
var hiddenPre=document.createElement("pre");
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
/** /**
* decodes all entities into regular string * decodes all entities into regular string
* @param value * @param value
* @returns {string} A string with decoded entities. * @returns {string} A string with decoded entities.
*/ */
var hiddenPre=document.createElement("pre");
function decodeEntities(value) { function decodeEntities(value) {
hiddenPre.innerHTML=value.replace(/</g,"&lt;"); if (!value) { return ''; }
return hiddenPre.innerText || hiddenPre.textContent || '';
// Note: IE8 does not preserve spaces at the start/end of innerHTML
// so we must capture them and reattach them afterward
var parts = spaceRe.exec(value);
var spaceBefore = parts[1];
var spaceAfter = parts[3];
var content = parts[2];
if (content) {
hiddenPre.innerHTML=content.replace(/</g,"&lt;");
// innerText depends on styling as it doesn't display hidden elements.
// Therefore, it's better to use textContent not to cause unnecessary
// reflows. However, IE<9 don't support textContent so the innerText
// fallback is necessary.
content = 'textContent' in hiddenPre ?
hiddenPre.textContent : hiddenPre.innerText;
}
return spaceBefore + content + spaceAfter;
} }
/** /**
@ -367,11 +416,16 @@ function decodeEntities(value) {
* resulting string can be safely inserted into attribute or * resulting string can be safely inserted into attribute or
* element text. * element text.
* @param value * @param value
* @returns escaped text * @returns {string} escaped text
*/ */
function encodeEntities(value) { function encodeEntities(value) {
return value. return value.
replace(/&/g, '&amp;'). replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function (value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value){ replace(NON_ALPHANUMERIC_REGEXP, function(value){
return '&#' + value.charCodeAt(0) + ';'; return '&#' + value.charCodeAt(0) + ';';
}). }).
@ -389,7 +443,7 @@ function encodeEntities(value) {
* comment: function(text) {} * comment: function(text) {}
* } * }
*/ */
function htmlSanitizeWriter(buf){ function htmlSanitizeWriter(buf, uriValidator){
var ignore = false; var ignore = false;
var out = angular.bind(buf, buf.push); var out = angular.bind(buf, buf.push);
return { return {
@ -403,7 +457,9 @@ function htmlSanitizeWriter(buf){
out(tag); out(tag);
angular.forEach(attrs, function(value, key){ angular.forEach(attrs, function(value, key){
var lkey=angular.lowercase(key); var lkey=angular.lowercase(key);
if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) { var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' '); out(' ');
out(key); out(key);
out('="'); out('="');
@ -435,14 +491,14 @@ function htmlSanitizeWriter(buf){
// define ngSanitize module and register $sanitize service // define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).value('$sanitize', $sanitize); angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/* global htmlSanitizeWriter: false */ /* global sanitizeText: false */
/** /**
* @ngdoc filter * @ngdoc filter
* @name ngSanitize.filter:linky * @name linky
* @function * @kind function
* *
* @description * @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
@ -458,20 +514,21 @@ angular.module('ngSanitize', []).value('$sanitize', $sanitize);
<span ng-bind-html="linky_expression | linky"></span> <span ng-bind-html="linky_expression | linky"></span>
* *
* @example * @example
<doc:example module="ngSanitize"> <example module="linkyExample" deps="angular-sanitize.js">
<doc:source> <file name="index.html">
<script> <script>
function Ctrl($scope) { angular.module('linkyExample', ['ngSanitize'])
$scope.snippet = .controller('ExampleController', ['$scope', function($scope) {
'Pretty text with some links:\n'+ $scope.snippet =
'http://angularjs.org/,\n'+ 'Pretty text with some links:\n'+
'mailto:us@somewhere.org,\n'+ 'http://angularjs.org/,\n'+
'another@somewhere.org,\n'+ 'mailto:us@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.'; 'another@somewhere.org,\n'+
$scope.snippetWithTarget = 'http://angularjs.org/'; 'and one more: ftp://127.0.0.1/.';
} $scope.snippetWithTarget = 'http://angularjs.org/';
}]);
</script> </script>
<div ng-controller="Ctrl"> <div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea> Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table> <table>
<tr> <tr>
@ -503,43 +560,44 @@ angular.module('ngSanitize', []).value('$sanitize', $sanitize);
<td><div ng-bind="snippet"></div></td> <td><div ng-bind="snippet"></div></td>
</tr> </tr>
</table> </table>
</doc:source> </file>
<doc:scenario> <file name="protractor.js" type="protractor">
it('should linkify the snippet with urls', function() { it('should linkify the snippet with urls', function() {
expect(using('#linky-filter').binding('snippet | linky')). expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links:&#10;' + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
'<a href="http://angularjs.org/">http://angularjs.org/</a>,&#10;' + 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>,&#10;' + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>,&#10;' +
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
}); });
it ('should not linkify snippet without the linky filter', function() { it('should not linkify snippet without the linky filter', function() {
expect(using('#escaped-html').binding('snippet')). expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
toBe("Pretty text with some links:\n" + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
"http://angularjs.org/,\n" + 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
"mailto:us@somewhere.org,\n" + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
"another@somewhere.org,\n" +
"and one more: ftp://127.0.0.1/.");
}); });
it('should update', function() { it('should update', function() {
input('snippet').enter('new http://link.'); element(by.model('snippet')).clear();
expect(using('#linky-filter').binding('snippet | linky')). element(by.model('snippet')).sendKeys('new http://link.');
toBe('new <a href="http://link">http://link</a>.'); expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); toBe('new http://link.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
.toBe('new http://link.');
}); });
it('should work with the target property', function() { it('should work with the target property', function() {
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")). expect(element(by.id('linky-target')).
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>'); element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
}); });
</doc:scenario> </file>
</doc:example> </example>
*/ */
angular.module('ngSanitize').filter('linky', function() { angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP = var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/, /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
MAILTO_REGEXP = /^mailto:/; MAILTO_REGEXP = /^mailto:/;
return function(text, target) { return function(text, target) {
@ -547,31 +605,43 @@ angular.module('ngSanitize').filter('linky', function() {
var match; var match;
var raw = text; var raw = text;
var html = []; var html = [];
// TODO(vojta): use $sanitize instead
var writer = htmlSanitizeWriter(html);
var url; var url;
var i; var i;
var properties = {};
if (angular.isDefined(target)) {
properties.target = target;
}
while ((match = raw.match(LINKY_URL_REGEXP))) { while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence // We can not end in these as they are sometimes found at the end of the sentence
url = match[0]; url = match[0];
// if we did not match ftp/http/mailto then assume mailto // if we did not match ftp/http/mailto then assume mailto
if (match[2] == match[3]) url = 'mailto:' + url; if (match[2] == match[3]) url = 'mailto:' + url;
i = match.index; i = match.index;
writer.chars(raw.substr(0, i)); addText(raw.substr(0, i));
properties.href = url; addLink(url, match[0].replace(MAILTO_REGEXP, ''));
writer.start('a', properties);
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
writer.end('a');
raw = raw.substring(i + match[0].length); raw = raw.substring(i + match[0].length);
} }
writer.chars(raw); addText(raw);
return html.join(''); return $sanitize(html.join(''));
function addText(text) {
if (!text) {
return;
}
html.push(sanitizeText(text));
}
function addLink(url, text) {
html.push('<a ');
if (angular.isDefined(target)) {
html.push('target="');
html.push(target);
html.push('" ');
}
html.push('href="');
html.push(url);
html.push('">');
addText(text);
html.push('</a>');
}
}; };
}); }]);
})(window, window.angular); })(window, window.angular);

View file

@ -1,14 +1,15 @@
/* /*
AngularJS v1.2.0 AngularJS v1.3.0-rc.1
(c) 2010-2012 Google, Inc. http://angularjs.org (c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(m,g,n){'use strict';function h(a){var d={};a=a.split(",");var c;for(c=0;c<a.length;c++)d[a[c]]=!0;return d}function D(a,d){function c(a,b,c,f){b=g.lowercase(b);if(r[b])for(;e.last()&&s[e.last()];)k("",e.last());t[b]&&e.last()==b&&k("",b);(f=u[b]||!!f)||e.push(b);var l={};c.replace(E,function(a,b,d,c,e){l[b]=p(d||c||e||"")});d.start&&d.start(b,l,f)}function k(a,b){var c=0,k;if(b=g.lowercase(b))for(c=e.length-1;0<=c&&e[c]!=b;c--);if(0<=c){for(k=e.length-1;k>=c;k--)d.end&&d.end(e[k]);e.length= (function(q,g,r){'use strict';function F(a){var d=[];t(d,g.noop).chars(a);return d.join("")}function m(a){var d={};a=a.split(",");var c;for(c=0;c<a.length;c++)d[a[c]]=!0;return d}function G(a,d){function c(a,b,c,h){b=g.lowercase(b);if(u[b])for(;f.last()&&v[f.last()];)e("",f.last());w[b]&&f.last()==b&&e("",b);(h=x[b]||!!h)||f.push(b);var n={};c.replace(H,function(a,b,d,c,e){n[b]=s(d||c||e||"")});d.start&&d.start(b,n,h)}function e(a,b){var c=0,e;if(b=g.lowercase(b))for(c=f.length-1;0<=c&&f[c]!=b;c--);
c}}var b,f,e=[],l=a;for(e.last=function(){return e[e.length-1]};a;){f=!0;if(e.last()&&v[e.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+e.last()+"[^>]*>","i"),function(a,b){b=b.replace(F,"$1").replace(G,"$1");d.chars&&d.chars(p(b));return""}),k("",e.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(d.comment&&d.comment(a.substring(4,b)),a=a.substring(b+3),f=!1);else if(w.test(a)){if(b=a.match(w))a=a.replace(b[0],""),f=!1}else if(H.test(a)){if(b=a.match(x))a= if(0<=c){for(e=f.length-1;e>=c;e--)d.end&&d.end(f[e]);f.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,l,f=[],n=a,h;for(f.last=function(){return f[f.length-1]};a;){h="";l=!0;if(f.last()&&y[f.last()])a=a.replace(new RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(I,"$1").replace(J,"$1");d.chars&&d.chars(s(b));return""}),e("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(d.comment&&d.comment(a.substring(4,
a.substring(b[0].length),b[0].replace(x,k),f=!1}else I.test(a)&&(b=a.match(y))&&(a=a.substring(b[0].length),b[0].replace(y,c),f=!1);f&&(b=a.indexOf("<"),f=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(p(f)))}if(a==l)throw J("badparse",a);l=a}k()}function p(a){q.innerHTML=a.replace(/</g,"&lt;");return q.innerText||q.textContent||""}function z(a){return a.replace(/&/g,"&amp;").replace(K,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function A(a){var d= b)),a=a.substring(b+3),l=!1);else if(z.test(a)){if(b=a.match(z))a=a.replace(b[0],""),l=!1}else if(K.test(a)){if(b=a.match(A))a=a.substring(b[0].length),b[0].replace(A,e),l=!1}else L.test(a)&&((b=a.match(B))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(B,c)),l=!1):(h+="<",a=a.substring(1)));l&&(b=a.indexOf("<"),h+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(s(h)))}if(a==n)throw M("badparse",a);n=a}e()}function s(a){if(!a)return"";var d=N.exec(a);a=d[1];var c=d[3];if(d=d[2])p.innerHTML=
!1,c=g.bind(a,a.push);return{start:function(a,b,f){a=g.lowercase(a);!d&&v[a]&&(d=a);d||!0!==B[a]||(c("<"),c(a),g.forEach(b,function(a,b){var d=g.lowercase(b);!0!==L[d]||!0===C[d]&&!a.match(M)||(c(" "),c(b),c('="'),c(z(a)),c('"'))}),c(f?"/>":">"))},end:function(a){a=g.lowercase(a);d||!0!==B[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(z(a))}}}var J=g.$$minErr("$sanitize"),y=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,x=/^<\s*\/\s*([\w:-]+)[^>]*>/, d.replace(/</g,"&lt;"),d="textContent"in p?p.textContent:p.innerText;return a+d+c}function C(a){return a.replace(/&/g,"&amp;").replace(O,function(a){var c=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(c-55296)+(a-56320)+65536)+";"}).replace(P,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function t(a,d){var c=!1,e=g.bind(a,a.push);return{start:function(a,l,f){a=g.lowercase(a);!c&&y[a]&&(c=a);c||!0!==D[a]||(e("<"),e(a),g.forEach(l,function(c,f){var k=
E=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,I=/^</,H=/^<\s*\//,F=/\x3c!--(.*?)--\x3e/g,w=/<!DOCTYPE([^>]*?)>/i,G=/<!\[CDATA\[(.*?)]]\x3e/g,M=/^((ftp|https?):\/\/|mailto:|tel:|#)/i,K=/([^\#-~| |!])/g,u=h("area,br,col,hr,img,wbr");m=h("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");n=h("rp,rt");var t=g.extend({},n,m),r=g.extend({},m,h("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")), g.lowercase(f),l="img"===a&&"src"===k||"background"===k;!0!==Q[k]||!0===E[k]&&!d(c,l)||(e(" "),e(f),e('="'),e(C(c)),e('"'))}),e(f?"/>":">"))},end:function(a){a=g.lowercase(a);c||!0!==D[a]||(e("</"),e(a),e(">"));a==c&&(c=!1)},chars:function(a){c||e(C(a))}}}var M=g.$$minErr("$sanitize"),B=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,A=/^<\/\s*([\w:-]+)[^>]*>/,H=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,L=/^</,
s=g.extend({},n,h("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),v=h("script,style"),B=g.extend({},u,r,s,t),C=h("background,cite,href,longdesc,src,usemap"),L=g.extend({},C,h("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")), K=/^<\//,I=/\x3c!--(.*?)--\x3e/g,z=/<!DOCTYPE([^>]*?)>/i,J=/<!\[CDATA\[(.*?)]]\x3e/g,O=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,P=/([^\#-~| |!])/g,x=m("area,br,col,hr,img,wbr");q=m("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");r=m("rp,rt");var w=g.extend({},r,q),u=g.extend({},q,m("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),v=g.extend({},r,m("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
q=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[];D(a,A(d));return d.join("")});g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,d=/^mailto:/;return function(c,k){if(!c)return c;var b,f=c,e=[],l=A(e),h,m,n={};g.isDefined(k)&&(n.target=k);for(;b=f.match(a);)h=b[0],b[2]==b[3]&&(h="mailto:"+h),m=b.index,l.chars(f.substr(0,m)),n.href=h,l.start("a",n),l.chars(b[0].replace(d,"")),l.end("a"), y=m("script,style"),D=g.extend({},x,u,v,w),E=m("background,cite,href,longdesc,src,usemap"),Q=g.extend({},E,m("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,target,title,type,valign,value,vspace,width")),p=document.createElement("pre"),N=/^(\s*)([\s\S]*?)(\s*)$/;g.module("ngSanitize",[]).provider("$sanitize",
f=f.substring(m+b[0].length);l.chars(f);return e.join("")}})})(window,window.angular); function(){this.$get=["$$sanitizeUri",function(a){return function(d){var c=[];G(d,t(c,function(c,b){return!/^unsafe/.test(a(c,b))}));return c.join("")}}]});g.module("ngSanitize").filter("linky",["$sanitize",function(a){var d=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,c=/^mailto:/;return function(e,b){function l(a){a&&k.push(F(a))}function f(a,c){k.push("<a ");g.isDefined(b)&&(k.push('target="'),k.push(b),k.push('" '));k.push('href="');k.push(a);k.push('">');l(c);k.push("</a>")}
if(!e)return e;for(var n,h=e,k=[],m,p;n=h.match(d);)m=n[0],n[2]==n[3]&&(m="mailto:"+m),p=n.index,l(h.substr(0,p)),f(m,n[0].replace(c,"")),h=h.substring(p+n[0].length);l(h);return a(k.join(""))}}])})(window,window.angular);
//# sourceMappingURL=angular-sanitize.min.js.map //# sourceMappingURL=angular-sanitize.min.js.map

7909
webclient/js/angular.js vendored

File diff suppressed because it is too large Load diff

View file

@ -1,200 +1,237 @@
/* /*
AngularJS v1.2.0 AngularJS v1.3.0-rc.1
(c) 2010-2012 Google, Inc. http://angularjs.org (c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT License: MIT
*/ */
(function(W,O,s){'use strict';function L(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/undefined/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function pb(b){if(null==b||ya(b))return!1; (function(t,Y,s){'use strict';function K(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.0-rc.1/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var d=encodeURIComponent,e;e=arguments[a];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return Error(c)}}function Na(b){if(null==b||Oa(b))return!1;var a=b.length;return 1===b.nodeType&&
var a=b.length;return 1===b.nodeType&&a?!0:D(b)||J(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(B(b))for(d in b)"prototype"!=d&&("length"!=d&&"name"!=d&&b.hasOwnProperty(d))&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(pb(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Nb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Jc(b,a,c){for(var d= a?!0:C(b)||L(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d,e;if(b)if(D(b))for(d in b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d)||a.call(c,b[d],d,b);else if(L(b)||Na(b)){var f="object"!==typeof b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else if(b.forEach&&b.forEach!==r)b.forEach(a,c,b);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d,b);return b}function Zb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}
Nb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Ob(b){return function(a,c){b(c,a)}}function Za(){for(var b=ia.length,a;b;){b--;a=ia[b].charCodeAt(0);if(57==a)return ia[b]="A",ia.join("");if(90==a)ia[b]="0";else return ia[b]=String.fromCharCode(a+1),ia.join("")}ia.unshift("0");return ia.join("")}function Pb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function u(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});Pb(b,a);return b}function U(b){return parseInt(b, function od(b,a,c){for(var d=Zb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function $b(b){return function(a,c){b(c,a)}}function pd(){return++bb}function ac(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function E(b){for(var a=b.$$hashKey,c=1,d=arguments.length;c<d;c++){var e=arguments[c];if(e)for(var f=Object.keys(e),g=0,h=f.length;g<h;g++){var m=f[g];b[m]=e[m]}}ac(b,a);return b}function U(b){return parseInt(b,10)}function bc(b,a){return E(new (E(function(){},{prototype:b})),a)}function w(){}
10)}function Qb(b,a){return u(new (u(function(){},{prototype:b})),a)}function t(){}function za(b){return b}function aa(b){return function(){return b}}function x(b){return"undefined"==typeof b}function z(b){return"undefined"!=typeof b}function T(b){return null!=b&&"object"==typeof b}function D(b){return"string"==typeof b}function qb(b){return"number"==typeof b}function Ja(b){return"[object Date]"==Ka.apply(b)}function J(b){return"[object Array]"==Ka.apply(b)}function B(b){return"function"==typeof b} function Pa(b){return b}function da(b){return function(){return b}}function F(b){return"undefined"===typeof b}function B(b){return"undefined"!==typeof b}function S(b){return null!==b&&"object"===typeof b}function C(b){return"string"===typeof b}function ea(b){return"number"===typeof b}function fa(b){return"[object Date]"===Ga.call(b)}function D(b){return"function"===typeof b}function cb(b){return"[object RegExp]"===Ga.call(b)}function Oa(b){return b&&b.window===b}function Qa(b){return b&&b.$evalAsync&&
function $a(b){return"[object RegExp]"==Ka.apply(b)}function ya(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function Kc(b){return b&&(b.nodeName||b.on&&b.find)}function Lc(b,a,c){var d=[];q(b,function(b,f,g){d.push(a.call(c,b,f,g))});return d}function ab(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function La(b,a){var c=ab(b,a);0<=c&&b.splice(c,1);return a}function da(b,a){if(ya(b)||b&&b.$evalAsync&&b.$watch)throw Ma("cpws");if(a){if(b=== b.$watch}function qd(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function rd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function pa(b){return P(b.nodeName||b[0].nodeName)}function sd(b,a,c){var d=[];r(b,function(b,f,g){d.push(a.call(c,b,f,g))});return d}function Ra(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return a}function Ha(b,a,c,d){if(Oa(b)||Qa(b))throw Sa("cpws");if(a){if(b===a)throw Sa("cpi");c=c||[];d=d||[];if(S(b)){var e=c.indexOf(b);if(-1!==e)return d[e];
a)throw Ma("cpi");if(J(b))for(var c=a.length=0;c<b.length;c++)a.push(da(b[c]));else{c=a.$$hashKey;q(a,function(b,c){delete a[c]});for(var d in b)a[d]=da(b[d]);Pb(a,c)}}else(a=b)&&(J(b)?a=da(b,[]):Ja(b)?a=new Date(b.getTime()):$a(b)?a=RegExp(b.source):T(b)&&(a=da(b,{})));return a}function Mc(b,a){a=a||{};for(var c in b)b.hasOwnProperty(c)&&"$$"!==c.substr(0,2)&&(a[c]=b[c]);return a}function Aa(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&& c.push(b);d.push(a)}if(L(b))for(var f=a.length=0;f<b.length;f++)e=Ha(b[f],null,c,d),S(b[f])&&(c.push(b[f]),d.push(e)),a.push(e);else{var g=a.$$hashKey;L(a)?a.length=0:r(a,function(c,b){delete a[b]});for(f in b)b.hasOwnProperty(f)&&(e=Ha(b[f],null,c,d),S(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e);ac(a,g)}}else if(a=b)L(b)?a=Ha(b,[],c,d):fa(b)?a=new Date(b.getTime()):cb(b)?(a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):S(b)&&(e=Object.create(Object.getPrototypeOf(b)),
"object"==c)if(J(b)){if(!J(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!Aa(b[d],a[d]))return!1;return!0}}else{if(Ja(b))return Ja(a)&&b.getTime()==a.getTime();if($a(b)&&$a(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||ya(b)||ya(a)||J(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!B(b[d])){if(!Aa(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!B(a[d]))return!1;return!0}return!1}function Rb(){return O.securityPolicy&& a=Ha(b,e,c,d));return a}function qa(b,a){if(L(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(S(b))for(c in a=a||{},b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ra(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(L(b)){if(!L(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ra(b[d],a[d]))return!1;return!0}}else{if(fa(b))return fa(a)?ra(b.getTime(),a.getTime()):!1;if(cb(b)&&
O.securityPolicy.isActive||O.querySelector&&!(!O.querySelector("[ng-csp]")&&!O.querySelector("[data-ng-csp]"))}function rb(b,a){var c=2<arguments.length?ta.call(arguments,2):[];return!B(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(ta.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Nc(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=s:ya(a)?c="$WINDOW":a&&O===a?c="$DOCUMENT":a&&(a.$evalAsync&& cb(a))return b.toString()==a.toString();if(Qa(b)||Qa(a)||Oa(b)||Oa(a)||L(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!D(b[d])){if(!ra(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!D(a[d]))return!1;return!0}return!1}function db(b,a,c){return b.concat(Ta.call(a,c))}function cc(b,a){var c=2<arguments.length?Ta.call(arguments,2):[];return!D(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(Ta.call(arguments,0))):
a.$watch)&&(c="$SCOPE");return c}function ma(b,a){return"undefined"===typeof b?s:JSON.stringify(b,Nc,a?" ":null)}function Sb(b){return D(b)?JSON.parse(b):b}function Na(b){b&&0!==b.length?(b=w(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function ea(b){b=y(b).clone();try{b.html("")}catch(a){}var c=y("<div>").append(b).html();try{return 3===b[0].nodeType?w(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+w(b)})}catch(d){return w(c)}}function Tb(b){try{return decodeURIComponent(b)}catch(a){}} a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function td(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)&&"$"===b.charAt(1)?c=s:Oa(a)?c="$WINDOW":a&&Y===a?c="$DOCUMENT":Qa(a)&&(c="$SCOPE");return c}function sa(b,a){return"undefined"===typeof b?s:JSON.stringify(b,td,a?" ":null)}function dc(b){return C(b)?JSON.parse(b):b}function ta(b){b=G(b).clone();try{b.empty()}catch(a){}var c=G("<div>").append(b).html();try{return 3===b[0].nodeType?P(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,
function Ub(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=Tb(c[0]),z(d)&&(b=z(c[1])?Tb(c[1]):!0,a[d]?J(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Vb(b){var a=[];q(b,function(b,d){J(b)?q(b,function(b){a.push(ua(d,!0)+(!0===b?"":"="+ua(b,!0)))}):a.push(ua(d,!0)+(!0===b?"":"="+ua(b,!0)))});return a.length?a.join("&"):""}function sb(b){return ua(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ua(b,a){return encodeURIComponent(b).replace(/%40/gi, function(a,c){return"<"+P(c)})}catch(d){return P(c)}}function ec(b){try{return decodeURIComponent(b)}catch(a){}}function fc(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=ec(c[0]),B(d)&&(b=B(c[1])?ec(c[1]):!0,Ab.call(a,d)?L(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Bb(b){var a=[];r(b,function(b,d){L(b)?r(b,function(b){a.push(Da(d,!0)+(!0===b?"":"="+Da(b,!0)))}):a.push(Da(d,!0)+(!0===b?"":"="+Da(b,!0)))});return a.length?a.join("&"):""}
"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Oc(b,a){function c(a){a&&d.push(a)}var d=[b],e,f,g=["ng:app","ng-app","x-ng-app","data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(g,function(a){g[a]=!0;c(O.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+a+"\\:"),c),q(b.querySelectorAll("["+a+"]"),c))});q(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,f= function eb(b){return Da(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Da(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function ud(b,a){var c,d,e=fb.length;b=G(b);for(d=0;d<e;++d)if(c=fb[d]+a,C(c=b.attr(c)))return c;return null}function vd(b,a){var c,d,e={};r(fb,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});
(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&g[b.name]&&(e=a,f=b.value)})}});e&&a(e,f?[f]:[])}function Wb(b,a){var c=function(){b=y(b);if(b.injector()){var c=b[0]===O?"document":ea(b);throw Ma("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=Xb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/; r(fb,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==ud(c,"strict-di"),a(c,d?[d]:[],e))}function gc(b,a,c){S(c)||(c={});c=E({strictDi:!1},c);var d=function(){b=G(b);if(b.injector()){var d=b[0]===Y?"document":ta(b);throw Sa("btstrpd",d.replace(/</,"&lt;").replace(/>/,"&gt;"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);
if(W&&!d.test(W.name))return c();W.name=W.name.replace(d,"");bb.resumeBootstrap=function(b){q(b,function(b){a.push(b)});c()}}function cb(b,a){a=a||"_";return b.replace(Pc,function(b,d){return(d?a:"")+b.toLowerCase()})}function tb(b,a,c){if(!b)throw Ma("areq",a||"?",c||"required");return b}function Oa(b,a,c){c&&J(b)&&(b=b[b.length-1]);tb(B(b),a,"not a function, got "+(b&&"object"==typeof b?b.constructor.name||"Object":typeof b));return b}function na(b,a){if("hasOwnProperty"===b)throw Ma("badname", a.unshift("ng");d=Cb(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;t&&e.test(t.name)&&(c.debugInfoEnabled=!0,t.name=t.name.replace(e,""));if(t&&!f.test(t.name))return d();t.name=t.name.replace(f,"");Ea.resumeBootstrap=function(b){r(b,function(b){a.push(b)});d()}}function wd(){t.name="NG_ENABLE_DEBUG_INFO!"+t.name;t.location.reload()}function xd(b){return Ea.element(b).injector().get("$$testability")}
a);}function ub(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&B(b)?rb(e,b):b}function vb(b){if(b.startNode===b.endNode)return y(b.startNode);var a=b.startNode,c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b.endNode);return y(c)}function Qc(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=L("$injector");return a(a(b,"angular",Object),"module",function(){var b={};return function(e,f,g){na(e,"module");f&&b.hasOwnProperty(e)&&(b[e]= function Db(b,a){a=a||"_";return b.replace(yd,function(b,d){return(d?a:"")+b.toLowerCase()})}function zd(){var b;hc||((la=t.jQuery)&&la.fn.on?(G=la,E(la.fn,{scope:Ia.scope,isolateScope:Ia.isolateScope,controller:Ia.controller,injector:Ia.injector,inheritedData:Ia.inheritedData}),b=la.cleanData,la.cleanData=function(a){var c;if(Eb)Eb=!1;else for(var d=0,e;null!=(e=a[d]);d++)(c=la._data(e,"events"))&&c.$destroy&&la(e).triggerHandler("$destroy");b(a)}):G=V,Ea.element=G,hc=!0)}function Fb(b,a,c){if(!b)throw Sa("areq",
null);return a(b,e,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return n}}if(!f)throw c("nomod",e);var b=[],d=[],l=a("$injector","invoke"),n={_invokeQueue:b,_runBlocks:d,requires:f,name:e,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animateProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider", a||"?",c||"required");return b}function gb(b,a,c){c&&L(b)&&(b=b[b.length-1]);Fb(D(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ja(b,a){if("hasOwnProperty"===b)throw Sa("badname",a);}function ic(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&D(b)?cc(e,b):b}function hb(b){var a=b[0];b=b[b.length-1];var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return G(c)}function Ad(b){function a(a,
"register"),directive:a("$compileProvider","directive"),config:l,run:function(a){d.push(a);return this}};g&&l(g);return n})}})}function Pa(b){return b.replace(Rc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(Sc,"Moz$1")}function wb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:[this],m=a,k,l,n,r,p,C;if(!d||null!=b)for(;e.length;)for(k=e.shift(),l=0,n=k.length;l<n;l++)for(r=y(k[l]),m?r.triggerHandler("$destroy"):m=!m,p=0,r=(C=r.children()).length;p<r;p++)e.push(Ba(C[p]));return f.apply(this, b,c){return a[b]||(a[b]=c())}var c=K("$injector"),d=K("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||K;return a(b,"module",function(){var b={};return function(f,g,h){if("hasOwnProperty"===f)throw d("badname","module");g&&b.hasOwnProperty(f)&&(b[f]=null);return a(b,f,function(){function a(c,d,e,f){f||(f=b);return function(){f[e||"push"]([c,d,arguments]);return q}}if(!g)throw c("nomod",f);var b=[],d=[],e=[],l=a("$injector","invoke","push",d),q={_invokeQueue:b,_configBlocks:d,_runBlocks:e,requires:g,
arguments)}var f=Ba.fn[b],f=f.$original||f;e.$original=f;Ba.fn[b]=e}function Q(b){if(b instanceof Q)return b;if(!(this instanceof Q)){if(D(b)&&"<"!=b.charAt(0))throw xb("nosel");return new Q(b)}if(D(b)){var a=O.createElement("div");a.innerHTML="<div>&#160;</div>"+b;a.removeChild(a.firstChild);yb(this,a.childNodes);y(O.createDocumentFragment()).append(this)}else yb(this,b)}function zb(b){return b.cloneNode(!0)}function Qa(b){Yb(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Qa(b[a])}function Zb(b, name:f,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animateProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:l,run:function(a){e.push(a);return this}};h&&l(h);return q})}})}function Bd(b){E(b,{bootstrap:gc,copy:Ha,extend:E,equals:ra,element:G,forEach:r,
a,c,d){if(z(d))throw xb("offargs");var e=ja(b,"events");ja(b,"handle")&&(x(a)?q(e,function(a,c){Ab(b,c,a);delete e[c]}):q(a.split(" "),function(a){x(c)?(Ab(b,a,e[a]),delete e[a]):La(e[a]||[],c)}))}function Yb(b,a){var c=b[db],d=Ra[c];d&&(a?delete Ra[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Zb(b)),delete Ra[c],b[db]=s))}function ja(b,a,c){var d=b[db],d=Ra[d||-1];if(z(c))d||(b[db]=d=++Tc,d=Ra[d]={}),d[a]=c;else return d&&d[a]}function $b(b,a,c){var d=ja(b,"data"),e=z(c),f=!e&& injector:Cb,noop:w,bind:cc,toJson:sa,fromJson:dc,identity:Pa,isUndefined:F,isDefined:B,isString:C,isFunction:D,isObject:S,isNumber:ea,isElement:qd,isArray:L,version:Cd,isDate:fa,lowercase:P,uppercase:ib,callbacks:{counter:0},getTestability:xd,$$minErr:K,$$csp:Ua,reloadWithDebugInfo:wd,$$hasClass:jb});Va=Ad(t);try{Va("ngLocale")}catch(a){Va("ngLocale",[]).provider("$locale",Dd)}Va("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Ed});a.provider("$compile",jc).directive({a:Fd,input:kc,
z(a),g=f&&!T(a);d||g||ja(b,"data",d={});if(e)d[a]=c;else if(f){if(g)return d&&d[a];u(d,a)}else return d}function Bb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Cb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",Y((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+Y(a)+" "," ")))})}function Db(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g, textarea:kc,form:Gd,script:Hd,select:Id,style:Jd,option:Kd,ngBind:Ld,ngBindHtml:Md,ngBindTemplate:Nd,ngClass:Od,ngClassEven:Pd,ngClassOdd:Qd,ngCloak:Rd,ngController:Sd,ngForm:Td,ngHide:Ud,ngIf:Vd,ngInclude:Wd,ngInit:Xd,ngNonBindable:Yd,ngPluralize:Zd,ngRepeat:$d,ngShow:ae,ngStyle:be,ngSwitch:ce,ngSwitchWhen:de,ngSwitchDefault:ee,ngOptions:fe,ngTransclude:ge,ngModel:he,ngList:ie,ngChange:je,pattern:lc,ngPattern:lc,required:mc,ngRequired:mc,minlength:nc,ngMinlength:nc,maxlength:oc,ngMaxlength:oc,ngValue:ke,
" ");q(a.split(" "),function(a){a=Y(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",Y(c))}}function yb(b,a){if(a){a=a.nodeName||!z(a.length)||ya(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function ac(b,a){return eb(b,"$"+(a||"ngController")+"Controller")}function eb(b,a,c){b=y(b);9==b[0].nodeType&&(b=b.find("html"));for(a=J(a)?a:[a];b.length;){for(var d=0,e=a.length;d<e;d++)if((c=b.data(a[d]))!==s)return c;b=b.parent()}}function bc(b,a){var c=fb[a.toLowerCase()];return c&& ngModelOptions:le}).directive({ngInclude:me}).directive(kb).directive(pc);a.provider({$anchorScroll:ne,$animate:oe,$browser:pe,$cacheFactory:qe,$controller:re,$document:se,$exceptionHandler:te,$filter:qc,$interpolate:ue,$interval:ve,$http:we,$httpBackend:xe,$location:ye,$log:ze,$parse:Ae,$rootScope:Be,$q:Ce,$$q:De,$sce:Ee,$sceDelegate:Fe,$sniffer:Ge,$templateCache:He,$templateRequest:Ie,$$testability:Je,$timeout:Ke,$window:Le,$$rAF:Me,$$asyncCallback:Ne})}])}function Wa(b){return b.replace(Oe,function(a,
cc[b.nodeName]&&c}function Uc(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||O);if(x(c.defaultPrevented)){var f=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;f.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};q(a[e||c.type],function(a){a.call(b,c)});8>=P?(c.preventDefault= b,d,e){return e?d.toUpperCase():d}).replace(Pe,"Moz$1")}function rc(b){b=b.nodeType;return 1===b||!b||9===b}function sc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Gb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(Qe.exec(b)||["",""])[1].toLowerCase();d=ia[d]||ia._default;c.innerHTML=d[1]+b.replace(Re,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=db(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});
null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ca(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=b.$$hashKey():c===s&&(c=b.$$hashKey=Za()):c=b;return a+":"+c}function Sa(b){q(b,this.put,this)}function dc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(Vc,""),c=c.match(Wc),q(c[1].split(Xc),function(b){b.replace(Yc,function(b, return e}function V(b){if(b instanceof V)return b;var a;C(b)&&(b=ba(b),a=!0);if(!(this instanceof V)){if(a&&"<"!=b.charAt(0))throw Hb("nosel");return new V(b)}if(a){a=Y;var c;b=(c=Se.exec(b))?[a.createElement(c[1])]:(c=sc(b,a))?c.childNodes:[]}tc(this,b)}function Ib(b){return b.cloneNode(!0)}function lb(b,a){a||mb(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)mb(c[d])}function uc(b,a,c,d){if(B(d))throw Hb("offargs");var e=(d=nb(b))&&d.events;if(d&&d.handle)if(a)r(a.split(" "),
c,d){a.push(d)})})),b.$inject=a):J(b)?(c=b.length-1,Oa(b[c],"fn"),a=b.slice(0,c)):Oa(b,"fn",!0);return a}function Xb(b){function a(a){return function(b,c){if(T(b))q(b,Ob(a));else return a(b,c)}}function c(a,b){na(a,"service");if(B(b)||J(b))b=n.instantiate(b);if(!b.$get)throw Ta("pget",a);return l[a+h]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,f,h;q(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(D(a))for(c=Ua(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue, function(a){F(c)?(b.removeEventListener(a,e[a],!1),delete e[a]):Ra(e[a]||[],c)});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,e[a],!1),delete e[a]}function mb(b,a){var c=b.ng339,d=c&&ob[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),uc(b)),delete ob[c],b.ng339=s))}function nb(b,a){var c=b.ng339,c=c&&ob[c];a&&!c&&(b.ng339=c=++Te,c=ob[c]={events:{},data:{},handle:s});return c}function Jb(b,a,c){if(rc(b)){var d=B(c),e=!d&&a&&!S(a),f=!a;b=(b=nb(b,!e))&&b.data;
f=0,h=d.length;f<h;f++){var g=d[f],m=n.get(g[0]);m[g[1]].apply(m,g[2])}else B(a)?b.push(n.invoke(a)):J(a)?b.push(n.invoke(a)):Oa(a,"module")}catch(l){throw J(a)&&(a=a[a.length-1]),l.message&&(l.stack&&-1==l.stack.indexOf(l.message))&&(l=l.message+"\n"+l.stack),Ta("modulerr",a,l.stack||l.message||l);}}});return b}function f(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===g)throw Ta("cdep",m.join(" <- "));return a[d]}try{return m.unshift(d),a[d]=g,a[d]=b(d)}finally{m.shift()}}function d(a,b,e){var f= if(d)b[a]=c;else{if(f)return b;if(e)return b&&b[a];E(b,a)}}}function jb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Kb(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",ba((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+ba(a)+" "," ")))})}function Lb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");r(a.split(" "),function(a){a=
[],h=dc(a),g,k,m;k=0;for(g=h.length;k<g;k++){m=h[k];if("string"!==typeof m)throw Ta("itkn",m);f.push(e&&e.hasOwnProperty(m)?e[m]:c(m))}a.$inject||(a=a[g]);switch(b?-1:f.length){case 0:return a();case 1:return a(f[0]);case 2:return a(f[0],f[1]);case 3:return a(f[0],f[1],f[2]);case 4:return a(f[0],f[1],f[2],f[3]);case 5:return a(f[0],f[1],f[2],f[3],f[4]);case 6:return a(f[0],f[1],f[2],f[3],f[4],f[5]);case 7:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);case 8:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6], ba(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",ba(c))}}function tc(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var c=a.length;if("number"===typeof c&&a.window!==a){if(c)for(var d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function vc(b,a){return pb(b,"$"+(a||"ngController")+"Controller")}function pb(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=L(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=G.data(b,a[d]))!==s)return c;b=b.parentNode||11===b.nodeType&&b.host}}
f[7]);case 9:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]);case 10:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9]);default:return a.apply(b,f)}}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(J(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return T(e)||B(e)?e:c},get:c,annotate:dc,has:function(b){return l.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}var g={},h="Provider",m=[],k=new Sa,l={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a, function wc(b){for(lb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function xc(b,a){a||lb(b);var c=b.parentNode;c&&c.removeChild(b)}function yc(b,a){var c=qb[a.toLowerCase()];return c&&zc[pa(b)]&&c}function Ue(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Ac[a]}function Ve(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=a[e||c.type],g=f?f.length:0;if(g){1<g&&(f=qa(f));for(var h=0;h<g;h++)f[h].call(b,c)}};c.elem=b;return c}function Ka(b,a){var c=
["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,aa(b))}),constant:a(function(a,b){na(a,"constant");l[a]=b;r[a]=b}),decorator:function(a,b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=p.invoke(d,c);return p.invoke(b,null,{$delegate:a})}}}},n=l.$injector=f(l,function(){throw Ta("unpr",m.join(" <- "));}),r={},p=r.$injector=f(r,function(a){a=n.get(a+h);return p.invoke(a.$get,a)});q(e(b),function(a){p.invoke(a||t)});return p}function Zc(){var b=!0;this.disableAutoScrolling= b&&b.$$hashKey;if(c)return"function"===typeof c&&(c=b.$$hashKey()),c;c=typeof b;return c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||pd)():c+":"+b}function Xa(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function We(b){return(b=b.toString().replace(Bc,"").match(Cc))?"function("+(b[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function Mb(b,a,c){var d;if("function"===typeof b){if(!(d=b.$inject)){d=[];if(b.length){if(a)throw C(c)&&c||(c=b.name||We(b)),La("strictdi",
function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==w(a.nodeName)||(b=a)});return b}function f(){var b=c.hash(),d;b?(d=g.getElementById(b))?d.scrollIntoView():(d=e(g.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function $c(b,a,c,d){function e(a){try{a.apply(null,ta.call(arguments,1))}finally{if(C--, c);a=b.toString().replace(Bc,"");a=a.match(Cc);r(a[1].split(Xe),function(a){a.replace(Ye,function(a,b,c){d.push(c)})})}b.$inject=d}}else L(b)?(a=b.length-1,gb(b[a],"fn"),d=b.slice(0,a)):gb(b,"fn",!0);return d}function Cb(b,a){function c(a){return function(b,c){if(S(b))r(b,$b(a));else return a(b,c)}}function d(a,b){Ja(a,"service");if(D(b)||L(b))b=p.instantiate(b);if(!b.$get)throw La("pget",a);return n[a+"Provider"]=b}function e(a,b){return d(a,{$get:b})}function f(a){var b=[],c;r(a,function(a){function d(a){var b,
0===C)for(;H.length;)try{H.pop()()}catch(b){c.error(b)}}}function f(a,b){(function gb(){q(I,function(a){a()});A=b(gb,a)})()}function g(){v=null;S!=h.url()&&(S=h.url(),q(Z,function(a){a(h.url())}))}var h=this,m=a[0],k=b.location,l=b.history,n=b.setTimeout,r=b.clearTimeout,p={};h.isMock=!1;var C=0,H=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){C++};h.notifyWhenNoOutstandingRequests=function(a){q(I,function(a){a()});0===C?a():H.push(a)};var I=[],A;h.addPollFn=function(a){x(A)&& c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=p.get(e[0]);f[e[1]].apply(f,e[2])}}if(!k.get(a)){k.put(a,!0);try{C(a)?(c=Va(a),b=b.concat(f(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):D(a)?b.push(p.invoke(a)):L(a)?b.push(p.invoke(a)):gb(a,"module")}catch(e){throw L(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),La("modulerr",a,e.stack||e.message||e);}}});return b}function g(b,c){function d(a){if(b.hasOwnProperty(a)){if(b[a]===
f(100,n);I.push(a);return a};var S=k.href,G=a.find("base"),v=null;h.url=function(a,c){k!==b.location&&(k=b.location);if(a){if(S!=a)return S=a,d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),G.attr("href",G.attr("href"))):(v=a,c?k.replace(a):k.href=a),h}else return v||k.href.replace(/%27/g,"'")};var Z=[],E=!1;h.onUrlChange=function(a){if(!E){if(d.history)y(b).on("popstate",g);if(d.hashchange)y(b).on("hashchange",g);else h.addPollFn(g);E=!0}Z.push(a);return a};h.baseHref=function(){var a= h)throw La("cdep",a+" <- "+m.join(" <- "));return b[a]}try{return m.unshift(a),b[a]=h,b[a]=c(a)}catch(e){throw b[a]===h&&delete b[a],e;}finally{m.shift()}}function e(b,c,f,h){"string"===typeof f&&(h=f,f=null);var g=[];h=Mb(b,a,h);var k,m,l;m=0;for(k=h.length;m<k;m++){l=h[m];if("string"!==typeof l)throw La("itkn",l);g.push(f&&f.hasOwnProperty(l)?f[l]:d(l))}L(b)&&(b=b[k]);return b.apply(c,g)}return{invoke:e,instantiate:function(a,b,c){var d=function(){};d.prototype=(L(a)?a[a.length-1]:a).prototype;
G.attr("href");return a?a.replace(/^https?\:\/\/[^\/]*/,""):""};var oa={},$="",va=h.baseHref();h.cookies=function(a,b){var d,e,f,h;if(a)b===s?m.cookie=escape(a)+"=;path="+va+";expires=Thu, 01 Jan 1970 00:00:00 GMT":D(b)&&(d=(m.cookie=escape(a)+"="+escape(b)+";path="+va).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==$)for($=m.cookie,d=$.split("; "),oa={},f=0;f<d.length;f++)e=d[f],h=e.indexOf("="),0<h&&(a= d=new d;a=e(a,d,b,c);return S(a)||D(a)?a:d},get:d,annotate:Mb,has:function(a){return n.hasOwnProperty(a+"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var h={},m=[],k=new Xa([],!0),n={$provide:{provider:c(d),factory:c(e),service:c(function(a,b){return e(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return e(a,da(b))}),constant:c(function(a,b){Ja(a,"constant");n[a]=b;l[a]=b}),decorator:function(a,b){var c=p.get(a+"Provider"),d=c.$get;c.$get=function(){var a=q.invoke(d,
unescape(e.substring(0,h)),oa[a]===s&&(oa[a]=unescape(e.substring(h+1))));return oa}};h.defer=function(a,b){var c;C++;c=n(function(){delete p[c];e(a)},b||0);p[c]=!0;return c};h.defer.cancel=function(a){return p[a]?(delete p[a],r(a),e(t),!0):!1}}function bd(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new $c(b,d,a,c)}]}function cd(){this.$get=function(){function b(b,d){function e(a){a!=n&&(r?r==a&&(r=a.n):r=a,f(a.n,a.p),f(a,n),n=a,n.n=null)}function f(a,b){a!=b&&(a&& c);return q.invoke(b,null,{$delegate:a})}}}},p=n.$injector=g(n,function(){throw La("unpr",m.join(" <- "));}),l={},q=l.$injector=g(l,function(a){var b=p.get(a+"Provider");return q.invoke(b.$get,b,s,a)});r(f(b),function(a){q.invoke(a||w)});return q}function ne(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;r(a,function(a){b||"a"!==pa(a)||(b=a)});return b}function f(){var b=c.hash(),d;b?(d=g.getElementById(b))?
(a.p=b),b&&(b.n=a))}if(b in a)throw L("$cacheFactory")("iid",b);var g=0,h=u({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,l={},n=null,r=null;return a[b]={put:function(a,b){var c=l[a]||(l[a]={key:a});e(c);if(!x(b))return a in m||g++,m[a]=b,g>k&&this.remove(r.key),b},get:function(a){var b=l[a];if(b)return e(b),m[a]},remove:function(a){var b=l[a];b&&(b==n&&(n=b.p),b==r&&(r=b.n),f(b.n,b.p),delete l[a],delete m[a],g--)},removeAll:function(){m={};g=0;l={};n=r=null},destroy:function(){l=h=m=null;delete a[b]}, d.scrollIntoView():(d=e(g.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function Ne(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function Ze(b,a,c,d){function e(a){try{a.apply(null,Ta.call(arguments,1))}finally{if(A--,0===A)for(;u.length;)try{u.pop()()}catch(b){c.error(b)}}}function f(a,
info:function(){return u({},h,{size:g})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function dd(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function ec(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,f=/^\s*(https?|ftp|mailto|tel|file):/,g=/^\s*(https?|ftp|file):|data:image\//,h=/^(on[a-z]+|formaction)$/;this.directive=function k(d,e){na(d,"directive"); b){(function R(){r(x,function(a){a()});z=b(R,a)})()}function g(){y=null;T!=h.url()&&(T=h.url(),r(Q,function(a){a(h.url())}))}var h=this,m=a[0],k=b.location,n=b.history,p=b.setTimeout,l=b.clearTimeout,q={};h.isMock=!1;var A=0,u=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){A++};h.notifyWhenNoOutstandingRequests=function(a){r(x,function(a){a()});0===A?a():u.push(a)};var x=[],z;h.addPollFn=function(a){F(z)&&f(100,p);x.push(a);return a};var T=k.href,v=a.find("base"),y=
D(d)?(tb(e,"directiveFactory"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];q(a[d],function(a,f){try{var h=b.invoke(a);B(h)?h={compile:aa(h)}:!h.compile&&h.link&&(h.compile=aa(h.link));h.priority=h.priority||0;h.index=f;h.name=h.name||d;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"A";e.push(h)}catch(g){c(g)}});return e}])),a[d].push(e)):q(d,Ob(k));return this};this.aHrefSanitizationWhitelist=function(a){return z(a)?(f=a,this): null;h.url=function(a,c){k!==b.location&&(k=b.location);n!==b.history&&(n=b.history);if(a){if(T!=a)return T=a,d.history?c?n.replaceState(null,"",a):(n.pushState(null,"",a),v.attr("href",v.attr("href"))):(y=a,c?k.replace(a):k.href=a),h}else return y||k.href.replace(/%27/g,"'")};var Q=[],ca=!1;h.onUrlChange=function(a){if(!ca){if(d.history)G(b).on("popstate",g);if(d.hashchange)G(b).on("hashchange",g);else h.addPollFn(g);ca=!0}Q.push(a);return a};h.$$checkUrlChange=g;h.baseHref=function(){var a=v.attr("href");
f};this.imgSrcSanitizationWhitelist=function(a){return z(a)?(g=a,this):g};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate",function(b,l,n,r,p,C,H,I,A,S,G){function v(a,b,c,d,e){a instanceof y||(a=y(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});var f=E(a,b,a,c,d,e);return function(b,c){tb(b,"scope");for(var d=c?Da.clone.call(a):a,e=0,h= return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var J={},N="",M=h.baseHref();h.cookies=function(a,b){var d,e,f,h;if(a)b===s?m.cookie=encodeURIComponent(a)+"=;path="+M+";expires=Thu, 01 Jan 1970 00:00:00 GMT":C(b)&&(d=(m.cookie=encodeURIComponent(a)+"="+encodeURIComponent(b)+";path="+M).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==N)for(N=m.cookie,d=N.split("; "),J={},f=0;f<d.length;f++)e=d[f],h=e.indexOf("="),
d.length;e<h;e++){var g=d[e];1!=g.nodeType&&9!=g.nodeType||d.eq(e).data("$scope",b)}Z(d,"ng-scope");c&&c(d,b);f&&f(b,d,d);return d}}function Z(a,b){try{a.addClass(b)}catch(c){}}function E(a,b,c,d,e,f){function h(a,c,d,e){var f,k,l,n,p,r,C,X=[];p=0;for(r=c.length;p<r;p++)X.push(c[p]);C=p=0;for(r=g.length;p<r;C++)k=X[C],c=g[p++],f=g[p++],l=y(k),c?(c.scope?(n=a.$new(),l.data("$scope",n),Z(l,"ng-scope")):n=a,(l=c.transclude)||!e&&b?c(f,n,k,d,function(b){return function(c){var d=a.$new();d.$$transcluded= 0<h&&(a=decodeURIComponent(e.substring(0,h)),J[a]===s&&(J[a]=decodeURIComponent(e.substring(h+1))));return J}};h.defer=function(a,b){var c;A++;c=p(function(){delete q[c];e(a)},b||0);q[c]=!0;return c};h.defer.cancel=function(a){return q[a]?(delete q[a],l(a),e(w),!0):!1}}function pe(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new Ze(b,d,a,c)}]}function qe(){this.$get=function(){function b(b,d){function e(a){a!=p&&(l?l==a&&(l=a.n):l=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}
!0;return b(d,c).on("$destroy",rb(d,d.$destroy))}}(l||b)):c(f,n,k,s,e)):f&&f(a,k.childNodes,s,e)}for(var g=[],k,l,n,p=0;p<a.length;p++)l=new R,k=oa(a[p],[],l,0===p?d:s,e),k=(f=k.length?M(k,a[p],l,b,c,null,[],[],f):null)&&f.terminal||!a[p].childNodes||!a[p].childNodes.length?null:E(a[p].childNodes,f?f.transclude:b),g.push(f),g.push(k),n=n||f||k,f=null;return n?h:null}function oa(a,b,c,f,h){var g=c.$attr,k;switch(a.nodeType){case 1:N(b,ka(Ea(a).toLowerCase()),"E",f,h);var l,n,p;k=a.attributes;for(var r= function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw K("$cacheFactory")("iid",b);var g=0,h=E({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,l=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}if(!F(b))return a in m||g++,m[a]=b,g>k&&this.remove(l.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return m[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==p&&(p=b.p);b==l&&(l=b.n);f(b.n,
0,C=k&&k.length;r<C;r++){var H=!1,v=!1;l=k[r];if(!P||8<=P||l.specified){n=l.name;p=ka(n);Fa.test(p)&&(n=cb(p.substr(6),"-"));var q=p.replace(/(Start|End)$/,"");p===q+"Start"&&(H=n,v=n.substr(0,n.length-5)+"end",n=n.substr(0,n.length-6));p=ka(n.toLowerCase());g[p]=n;c[p]=l=Y(P&&"href"==n?decodeURIComponent(a.getAttribute(n,2)):l.value);bc(a,p)&&(c[p]=!0);w(a,b,l,p);N(b,p,"A",f,h,H,v)}}a=a.className;if(D(a)&&""!==a)for(;k=e.exec(a);)p=ka(k[2]),N(b,p,"C",f,h)&&(c[p]=Y(k[3])),a=a.substr(k.index+k[0].length); b.p);delete n[a]}delete m[a];g--},removeAll:function(){m={};g=0;n={};p=l=null},destroy:function(){n=h=m=null;delete a[b]},info:function(){return E({},h,{size:g})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function He(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function jc(b,a){var c={},d=/^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,e=/(([\d\w_\-]+)(?:\:([^;]+))?;?)/,f=rd("ngSrc,ngSrcset,src,srcset"),g=
break;case 3:t(b,a.nodeValue);break;case 8:try{if(k=d.exec(a.nodeValue))p=ka(k[1]),N(b,p,"M",f,h)&&(c[p]=Y(k[2]))}catch(S){}}b.sort(wa);return b}function $(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw fa("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return y(d)}function va(a,b,c){return function(d,e,f,h){e=$(e[0],b,c);return a(d,e,f,h)}}function M(a,b,c,d,e,f,h,k,g){function p(a,b,c, /^(on[a-z]+|formaction)$/;this.directive=function k(a,d){Ja(a,"directive");C(a)?(Fb(d,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,d){var e=[];r(c[a],function(c,f){try{var h=b.invoke(c);D(h)?h={compile:da(h)}:!h.compile&&h.link&&(h.compile=da(h.link));h.priority=h.priority||0;h.index=f;h.name=h.name||a;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";e.push(h)}catch(g){d(g)}});return e}])),c[a].push(d)):
d){if(a){c&&(a=va(a,c,d));a.require=F.require;if(G===F||F.$$isolateScope)a=Q(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=va(b,c,d));b.require=F.require;if(G===F||F.$$isolateScope)b=Q(b,{isolateScope:!0});k.push(b)}}function r(a,b){var c,d="data",e=!1;if(D(a)){for(;"^"==(c=a.charAt(0))||"?"==c;)a=a.substr(1),"^"==c&&(d="inheritedData"),e=e||"?"==c;c=b[d]("$"+a+"Controller");8==b[0].nodeType&&b[0].$$controller&&(c=c||b[0].$$controller,b[0].$$controller=null);if(!c&&!e)throw fa("ctreq",a,ba);}else J(a)&& r(a,$b(k));return this};this.aHrefSanitizationWhitelist=function(b){return B(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};var h=!0;this.debugInfoEnabled=function(a){return B(a)?(h=a,this):h};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",
(c=[],q(a,function(a){c.push(r(a,b))}));return c}function S(a,d,e,f,g){var p,X,v,E,I,K;p=b===e?c:Mc(c,new R(y(e),c.$attr));X=p.$$element;if(G){var va=/^\s*([@=&])(\??)\s*(\w*)\s*$/;f=y(e);K=d.$new(!0);N&&N===G.$$originalDirective?f.data("$isolateScope",K):f.data("$isolateScopeNoTemplate",K);Z(f,"ng-isolate-scope");q(G.scope,function(a,b){var c=a.match(va)||[],e=c[3]||b,f="?"==c[2],c=c[1],h,g,k;K.$$isolateBindings[b]=c+e;switch(c){case "@":p.$observe(e,function(a){K[b]=a});p.$$observers[e].$$scope= function(a,b,p,l,q,A,u,x,z,T,v){function y(a,b){try{a.addClass(b)}catch(c){}}function Q(a,b,c,d,e){a instanceof G||(a=G(a));r(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=G(b).wrap("<span></span>").parent()[0])});var f=ca(a,b,a,c,d,e);Q.$$addScopeClass(a);var h=null,g=a,k;return function(b,c,d,e,l){Fb(b,"scope");h||(h=(l=l&&l[0])?"foreignobject"!==pa(l)&&l.toString().match(/SVG/)?"svg":"html":"html");"html"!==h&&a[0]!==k&&(g=G(Nb(h,G("<div>").append(a).html())));k=a[0];l=c?Ia.clone.call(g):
d;p[e]&&(K[b]=l(p[e])(d));break;case "=":if(f&&!p[e])break;g=C(p[e]);k=g.assign||function(){h=K[b]=g(d);throw fa("nonassign",p[e],G.name);};h=K[b]=g(d);K.$watch(function(){var a=g(d);a!==K[b]&&(a!==h?h=K[b]=a:k(d,a=h=K[b]));return a});break;case "&":g=C(p[e]);K[b]=function(a){return g(d,a)};break;default:throw fa("iscp",G.name,b,a);}})}A&&q(A,function(a){var b={$scope:a===G||a.$$isolateScope?K:d,$element:X,$attrs:p,$transclude:g},c;I=a.controller;"@"==I&&(I=p[a.name]);c=H(I,b);8==X[0].nodeType?X[0].$$controller= g;if(d)for(var n in d)l.data("$"+n+"Controller",d[n].instance);Q.$$addScopeInfo(l,b);c&&c(l,b);f&&f(b,l,l,e);return l}}function ca(a,b,c,d,e,f){function h(a,c,d,e){var f,k,l,n,u,q,ua;if(p)for(ua=Array(c.length),n=0;n<g.length;n+=3)f=g[n],ua[f]=c[f];else ua=c;n=0;for(u=g.length;n<u;)k=ua[g[n++]],c=g[n++],f=g[n++],c?(c.scope?(l=a.$new(),Q.$$addScopeInfo(G(k),l)):l=a,q=c.transcludeOnThisElement?J(a,c.transclude,e,c.elementTranscludeOnThisElement):!c.templateOnThisElement&&e?e:!e&&b?J(a,b):null,c(f,l,
c:X.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});f=0;for(v=h.length;f<v;f++)try{E=h[f],E(E.isolateScope?K:d,X,p,E.require&&r(E.require,X))}catch(M){n(M,ea(X))}f=d;G&&(G.template||null===G.templateUrl)&&(f=K);a&&a(f,e.childNodes,s,g);for(f=k.length-1;0<=f;f--)try{E=k[f],E(E.isolateScope?K:d,X,p,E.require&&r(E.require,X))}catch($){n($,ea(X))}}g=g||{};var E=-Number.MAX_VALUE,I,A=g.controllerDirectives,G=g.newIsolateScopeDirective,N=g.templateDirective;g=g.transcludeDirective; k,d,q)):f&&f(a,k.childNodes,s,e)}for(var g=[],k,l,n,u,p,q=0;q<a.length;q++){k=new Ob;l=N(a[q],[],k,0===q?d:s,e);(f=l.length?H(l,a[q],k,b,c,null,[],[],f):null)&&f.scope&&Q.$$addScopeClass(k.$$element);k=f&&f.terminal||!(n=a[q].childNodes)||!n.length?null:ca(n,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)g.push(q,f,k),u=!0,p=p||f;f=null}return u?h:null}function J(a,b,c,d){return function(e,f,h,g){var k=!1;e||(e=a.$new(),k=e.$$transcluded=!0);f=b(e,f,h,c,g);if(k&&
for(var M=c.$$element=y(b),F,ba,t,wa=d,x,ga=0,w=a.length;ga<w;ga++){F=a[ga];var u=F.$$start,Fa=F.$$end;u&&(M=$(b,u,Fa));t=s;if(E>F.priority)break;if(t=F.scope)I=I||F,F.templateUrl||(Va("new/isolated scope",G,F,M),T(t)&&(G=F));ba=F.name;!F.templateUrl&&F.controller&&(t=F.controller,A=A||{},Va("'"+ba+"' controller",A[ba],F,M),A[ba]=F);if(t=F.transclude)F.$$tlb||(Va("transclusion",g,F,M),g=F),"element"==t?(E=F.priority,t=$(b,u,Fa),M=c.$$element=y(O.createComment(" "+ba+": "+c[ba]+" ")),b=M[0],L(e,y(ta.call(t, !d)f.on("$destroy",function(){e.$destroy()});return f}}function N(b,f,h,g,l){var n=h.$attr,u;switch(b.nodeType){case 1:R(f,va(pa(b)),"E",g,l);for(var p,q,T,A=b.attributes,z=0,v=A&&A.length;z<v;z++){var x=!1,J=!1;p=A[z];if(!X||8<=X||p.specified){u=p.name;p=ba(p.value);q=va(u);if(T=U.test(q))u=Db(q.substr(6),"-");var W=q.replace(/(Start|End)$/,""),r;a:{var H=W;if(c.hasOwnProperty(H)){r=void 0;for(var H=a.get(H+"Directive"),O=0,Q=H.length;O<Q;O++)if(r=H[O],r.multiElement){r=!0;break a}}r=!1}r&&q===W+
0)),b),wa=v(t,d,E,f&&f.name,{transcludeDirective:g})):(t=y(zb(b)).contents(),M.html(""),wa=v(t,d));if(F.template)if(Va("template",N,F,M),N=F,t=B(F.template)?F.template(M,c):F.template,t=fc(t),F.replace){f=F;t=y("<div>"+Y(t)+"</div>").contents();b=t[0];if(1!=t.length||1!==b.nodeType)throw fa("tplrt",ba,"");L(e,M,b);w={$attr:{}};t=oa(b,[],w);var P=a.splice(ga+1,a.length-(ga+1));G&&z(t);a=a.concat(t).concat(P);gb(c,w);w=a.length}else M.html(t);if(F.templateUrl)Va("template",N,F,M),N=F,F.replace&&(f= "Start"&&(x=u,J=u.substr(0,u.length-5)+"end",u=u.substr(0,u.length-6));q=va(u.toLowerCase());n[q]=u;if(T||!h.hasOwnProperty(q))h[q]=p,yc(b,q)&&(h[q]=!0);ya(b,f,p,q,T);R(f,q,"A",g,l,x,J)}}b=b.className;if(C(b)&&""!==b)for(;u=e.exec(b);)q=va(u[2]),R(f,q,"C",g,l)&&(h[q]=ba(u[3])),b=b.substr(u.index+u[0].length);break;case 3:t(f,b.nodeValue);break;case 8:try{if(u=d.exec(b.nodeValue))q=va(u[1]),R(f,q,"M",g,l)&&(h[q]=ba(u[2]))}catch(M){}}f.sort(F);return f}function M(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&
F),S=ad(a.splice(ga,a.length-ga),M,c,e,wa,h,k,{controllerDirectives:A,newIsolateScopeDirective:G,templateDirective:N,transcludeDirective:g}),w=a.length;else if(F.compile)try{x=F.compile(M,c,wa),B(x)?p(null,x,u,Fa):x&&p(x.pre,x.post,u,Fa)}catch(ed){n(ed,ea(M))}F.terminal&&(S.terminal=!0,E=Math.max(E,F.priority))}S.scope=I&&!0===I.scope;S.transclude=g&&wa;return S}function z(a){for(var b=0,c=a.length;b<c;b++)a[b]=Qb(a[b],{$$isolateScope:!0})}function N(d,e,f,h,g,l,p){if(e===g)return null;g=null;if(a.hasOwnProperty(e)){var r; a.hasAttribute(b)){do{if(!a)throw ja("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return G(d)}function O(a,b,c){return function(d,e,f,h,g){e=M(e[0],b,c);return a(d,e,f,h,g)}}function H(a,c,d,e,f,h,g,k,l){function u(a,b,c,d){if(a){c&&(a=O(a,c,d));a.require=I.require;a.directiveName=ha;if(y===I||I.$$isolateScope)a=ga(a,{isolateScope:!0});g.push(a)}if(b){c&&(b=O(b,c,d));b.require=I.require;b.directiveName=ha;if(y===
e=b.get(e+c);for(var C=0,H=e.length;C<H;C++)try{r=e[C],(h===s||h>r.priority)&&-1!=r.restrict.indexOf(f)&&(l&&(r=Qb(r,{$$start:l,$$end:p})),d.push(r),g=r)}catch(E){n(E)}}return g}function gb(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,f){"class"==f?(Z(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?e.attr("style",e.attr("style")+";"+b):"$"==f.charAt(0)||a.hasOwnProperty(f)|| I||I.$$isolateScope)b=ga(b,{isolateScope:!0});k.push(b)}}function T(a,b,c,d){var e,f="data",h=!1;if(C(b)){for(;"^"==(e=b.charAt(0))||"?"==e;)b=b.substr(1),"^"==e&&(f="inheritedData"),h=h||"?"==e;e=null;d&&"data"===f&&(e=d[b])&&(e=e.instance);e=e||c[f]("$"+b+"Controller");if(!e&&!h)throw ja("ctreq",b,a);}else L(b)&&(e=[],r(b,function(b){e.push(T(a,b,c,d))}));return e}function z(a,e,f,h,l){function u(a,b,c){var d;Qa(a)||(c=b,b=a,a=s);E&&(d=W);c||(c=E?M.parent():M);return l(a,b,d,c)}var p,v,ua,x,W,O,
(a[f]=b,d[f]=c[f])})}function ad(a,b,c,d,e,f,h,g){var k=[],n,l,C=b[0],H=a.shift(),v=u({},H,{templateUrl:null,transclude:null,replace:null,$$originalDirective:H}),I=B(H.templateUrl)?H.templateUrl(b,c):H.templateUrl;b.html("");r.get(S.getTrustedResourceUrl(I),{cache:p}).success(function(p){var r;p=fc(p);if(H.replace){p=y("<div>"+Y(p)+"</div>").contents();r=p[0];if(1!=p.length||1!==r.nodeType)throw fa("tplrt",H.name,I);p={$attr:{}};L(d,b,r);var S=oa(r,[],p);T(H.scope)&&z(S);a=S.concat(a);gb(c,p)}else r= M,R;c===f?(R=d,M=d.$$element):(M=G(f),R=new Ob(M,d));y&&(x=e.$new(!0));O=l&&u;J&&(H={},W={},r(J,function(a){var b={$scope:a===y||a.$$isolateScope?x:e,$element:M,$attrs:R,$transclude:O};ua=a.controller;"@"==ua&&(ua=R[a.name]);b=A(ua,b,!0,a.controllerAs);W[a.name]=b;E||M.data("$"+a.name+"Controller",b.instance);H[a.name]=b}));if(y){var N=/^\s*([@=&])(\??)\s*(\w*)\s*$/;Q.$$addScopeInfo(M,x,!0,!(ca&&(ca===y||ca===y.$$originalDirective)));Q.$$addScopeClass(M,!0);h=H&&H[y.name];var xa=x;h&&h.identifier&&
C,b.html(p);a.unshift(v);n=M(a,r,c,e,b,H,f,h,g);q(d,function(a,c){a==r&&(d[c]=b[0])});for(l=E(b[0].childNodes,e);k.length;){p=k.shift();var S=k.shift(),Z=k.shift(),G=k.shift(),A=b[0];S!==C&&(A=zb(r),L(Z,y(S),A));n(l,p,A,d,G)}k=null}).error(function(a,b,c,d){throw fa("tpload",d.url);});return function(a,b,c,d,e){k?(k.push(b),k.push(c),k.push(d),k.push(e)):n(l,b,c,d,e)}}function wa(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Va(a,b,c,d){if(b)throw fa("multidir", !0===y.bindToController&&(xa=h.instance);r(y.scope,function(a,c){var d=a.match(N)||[],f=d[3]||c,h="?"==d[2],d=d[1],g,k,l,u;x.$$isolateBindings[c]=d+f;switch(d){case "@":R.$observe(f,function(a){x[c]=a});R.$$observers[f].$$scope=e;R[f]&&(xa[c]=b(R[f])(e));break;case "=":if(h&&!R[f])break;k=q(R[f]);u=k.literal?ra:function(a,b){return a===b||a!==a&&b!==b};l=k.assign||function(){g=xa[c]=k(e);throw ja("nonassign",R[f],y.name);};g=xa[c]=k(e);h=e.$watch(q(R[f],function(a){u(a,xa[c])||(u(a,g)?l(e,a=xa[c]):
b.name,c.name,a,ea(d));}function t(a,b){var c=l(b,!0);c&&a.push({priority:0,compile:aa(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);Z(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function x(a,b){if("xlinkHref"==b||"IMG"!=Ea(a)&&("src"==b||"ngSrc"==b))return S.RESOURCE_URL}function w(a,b,c,d){var e=l(c,!0);if(e){if("multiple"===d&&"SELECT"===Ea(a))throw fa("selmulti",ea(a));b.push({priority:100,compile:function(){return{pre:function(b,c,f){c= xa[c]=a);return g=a}),null,k.literal);x.$on("$destroy",h);break;case "&":k=q(R[f]);xa[c]=function(a){return k(e,a)};break;default:throw ja("iscp",y.name,c,a);}})}H&&(r(H,function(a){a()}),H=null);h=0;for(p=g.length;h<p;h++)v=g[h],Dc(v,v.isolateScope?x:e,M,R,v.require&&T(v.directiveName,v.require,M,W),O);h=e;y&&(y.template||null===y.templateUrl)&&(h=x);a&&a(h,f.childNodes,s,l);for(h=k.length-1;0<=h;h--)v=k[h],Dc(v,v.isolateScope?x:e,M,R,v.require&&T(v.directiveName,v.require,M,W),O)}l=l||{};for(var v=
f.$$observers||(f.$$observers={});if(h.test(d))throw fa("nodomevents");if(e=l(f[d],!0,x(a,d)))f[d]=e(b),(c[d]||(c[d]=[])).$$inter=!0,(f.$$observers&&f.$$observers[d].$$scope||b).$watch(e,function(a){f.$set(d,a)})}}}})}}function L(a,b,c){var d=b[0],e=b.length,f=d.parentNode,h,g;if(a)for(h=0,g=a.length;h<g;h++)if(a[h]==d){a[h++]=c;g=h+e-1;for(var k=a.length;h<k;h++,g++)g<k?a[h]=a[g]:delete a[h];a.length-=e-1;break}f&&f.replaceChild(c,d);a=O.createDocumentFragment();a.appendChild(d);c[y.expando]=d[y.expando]; -Number.MAX_VALUE,x,J=l.controllerDirectives,H,y=l.newIsolateScopeDirective,ca=l.templateDirective,R=l.nonTlbTranscludeDirective,w=!1,F=!1,E=l.hasElementTranscludeDirective,aa=d.$$element=G(c),I,ha,t,P=e,za,ma=0,ya=a.length;ma<ya;ma++){I=a[ma];var U=I.$$start,X=I.$$end;U&&(aa=M(c,U,X));t=s;if(v>I.priority)break;if(t=I.scope)I.templateUrl||(S(t)?(K("new/isolated scope",y||x,I,aa),y=I):K("new/isolated scope",y,I,aa)),x=x||I;ha=I.name;!I.templateUrl&&I.controller&&(t=I.controller,J=J||{},K("'"+ha+"' controller",
d=1;for(e=b.length;d<e;d++)f=b[d],y(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function Q(a,b){return u(function(){return a.apply(null,arguments)},a,b)}var R=function(a,b){this.$$element=a;this.$attr=b||{}};R.prototype={$normalize:ka,$addClass:function(a){a&&0<a.length&&G.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&G.removeClass(this.$$element,a)},$set:function(a,b,c,d){function e(a,b){var c=[],d=a.split(/\s+/),f=b.split(/\s+/),h=0;a:for(;h<d.length;h++){for(var g= J[ha],I,aa),J[ha]=I);if(t=I.transclude)w=!0,I.$$tlb||(K("transclusion",R,I,aa),R=I),"element"==t?(E=!0,v=I.priority,t=aa,aa=d.$$element=G(Y.createComment(" "+ha+": "+d[ha]+" ")),c=aa[0],rb(f,Ta.call(t,0),c),P=Q(t,e,v,h&&h.name,{nonTlbTranscludeDirective:R})):(t=G(Ib(c)).contents(),aa.empty(),P=Q(t,e));if(I.template)if(F=!0,K("template",ca,I,aa),ca=I,t=D(I.template)?I.template(aa,d):I.template,t=V(t),I.replace){h=I;t=Gb.test(t)?G(Nb(I.templateNamespace,ba(t))):[];c=t[0];if(1!=t.length||1!==c.nodeType)throw ja("tplrt",
d[h],k=0;k<f.length;k++)if(g==f[k])continue a;c.push(g)}return c}if("class"==a)b=b||"",c=this.$$element.attr("class")||"",this.$removeClass(e(c,b).join(" ")),this.$addClass(e(b,c).join(" "));else{var h=bc(this.$$element[0],a);h&&(this.$$element.prop(a,b),d=h);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=cb(a,"-"));h=Ea(this.$$element);if("A"===h&&"href"===a||"IMG"===h&&"src"===a)if(!P||8<=P)h=xa(b).href,""!==h&&("href"===a&&!h.match(f)||"src"===a&&!h.match(g))&&(this[a]=b="unsafe:"+ ha,"");rb(f,aa,c);ya={$attr:{}};t=N(c,[],ya);var Z=a.splice(ma+1,a.length-(ma+1));y&&W(t);a=a.concat(t).concat(Z);B(d,ya);ya=a.length}else aa.html(t);if(I.templateUrl)F=!0,K("template",ca,I,aa),ca=I,I.replace&&(h=I),z=$e(a.splice(ma,a.length-ma),aa,d,f,w&&P,g,k,{controllerDirectives:J,newIsolateScopeDirective:y,templateDirective:ca,nonTlbTranscludeDirective:R}),ya=a.length;else if(I.compile)try{za=I.compile(aa,d,P),D(za)?u(null,za,U,X):za&&u(za.pre,za.post,U,X)}catch($){p($,ta(aa))}I.terminal&&(z.terminal=
h);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b))}(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){n(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);I.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var ba=l.startSymbol(),ga=l.endSymbol(),fc="{{"==ba||"}}"==ga?za:function(a){return a.replace(/\{\{/g,ba).replace(/}}/g,ga)},Fa=/^ngAttr[A-Z]/;return v}]}function ka(b){return Pa(b.replace(fd,""))} !0,v=Math.max(v,I.priority))}z.scope=x&&!0===x.scope;z.transcludeOnThisElement=w;z.elementTranscludeOnThisElement=E;z.templateOnThisElement=F;z.transclude=P;l.hasElementTranscludeDirective=E;return z}function W(a){for(var b=0,c=a.length;b<c;b++)a[b]=bc(a[b],{$$isolateScope:!0})}function R(b,d,e,f,h,g,l){if(d===h)return null;h=null;if(c.hasOwnProperty(d)){var n;d=a.get(d+"Directive");for(var u=0,q=d.length;u<q;u++)try{n=d[u],(f===s||f>n.priority)&&-1!=n.restrict.indexOf(e)&&(g&&(n=bc(n,{$$start:g,
function gd(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){na(a,"controller");T(a)?u(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,f){var g,h,m;D(e)&&(g=e.match(a),h=g[1],m=g[3],e=b.hasOwnProperty(h)?b[h]:ub(f.$scope,h,!0)||ub(d,h,!0),Oa(e,h,!0));g=c.instantiate(e,f);if(m){if(!f||"object"!=typeof f.$scope)throw L("$controller")("noscp",h||e.name,m);f.$scope[m]=g}return g}}]}function hd(){this.$get=["$window",function(b){return y(b.document)}]}function id(){this.$get= $$end:l})),b.push(n),h=n)}catch(T){p(T)}}return h}function B(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(y(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function $e(a,b,c,d,e,f,h,g){var k=[],n,u,p=b[0],q=a.shift(),
["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function gc(b){var a={},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=w(Y(b.substr(0,e)));d=Y(b.substr(e+1));c&&(a[c]=a[c]?a[c]+(", "+d):d)});return a}function hc(b){var a=T(b)?b:s;return function(c){a||(a=gc(b));return c?a[w(c)]||null:a}}function ic(b,a,c){if(B(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function jd(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"}, T=E({},q,{templateUrl:null,transclude:null,replace:null,$$originalDirective:q}),v=D(q.templateUrl)?q.templateUrl(b,c):q.templateUrl,A=q.templateNamespace;b.empty();l(z.getTrustedResourceUrl(v)).then(function(l){var z,x;l=V(l);if(q.replace){l=Gb.test(l)?G(Nb(A,ba(l))):[];z=l[0];if(1!=l.length||1!==z.nodeType)throw ja("tplrt",q.name,v);l={$attr:{}};rb(d,b,z);var O=N(z,[],l);S(q.scope)&&W(O);a=O.concat(a);B(c,l)}else z=p,b.html(l);a.unshift(T);n=H(a,z,c,e,b,q,f,h,g);r(d,function(a,c){a==z&&(d[c]=b[0])});
e=this.defaults={transformResponse:[function(d){D(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=Sb(d)));return d}],transformRequest:[function(a){return T(a)&&"[object File]"!==Ka.apply(a)?ma(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:d,put:d,patch:d},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},f=this.interceptors=[],g=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,r){function p(a){function c(a){var b= for(u=ca(b[0].childNodes,e);k.length;){l=k.shift();x=k.shift();var M=k.shift(),Q=k.shift(),O=b[0];if(x!==p){var R=x.className;g.hasElementTranscludeDirective&&q.replace||(O=Ib(z));rb(M,G(x),O);y(G(O),R)}x=n.transcludeOnThisElement?J(l,n.transclude,Q):Q;n(u,l,O,d,x)}k=null});return function(a,b,c,d,e){a=e;k?(k.push(b),k.push(c),k.push(d),k.push(a)):(n.transcludeOnThisElement&&(a=J(b,n.transclude,e)),n(u,b,c,d,a))}}function F(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?
u({},a,{data:ic(a.data,a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?b:n.reject(b)}var d={transformRequest:e.transformRequest,transformResponse:e.transformResponse},f=function(a){function b(a){var c;q(a,function(b,d){B(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=u({},a.headers),f,h,c=u({},c.common,c[w(a.method)]);b(c);b(d);a:for(f in c){a=w(f);for(h in d)if(w(h)===a)continue a;d[f]=c[f]}return d}(a);u(d,a);d.headers=f;d.method=Ga(d.method);(a=Eb(d.url)?b.cookies()[d.xsrfCookieName|| -1:1:a.index-b.index}function K(a,b,c,d){if(b)throw ja("multidir",b.name,c.name,a,ta(d));}function t(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&Q.$$addBindingClass(a);return function(a,c){var e=c.parent();b||Q.$$addBindingClass(e);Q.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Nb(a,b){a=P(a||"html");switch(a){case "svg":case "math":var c=Y.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;
e.xsrfCookieName]:s)&&(f[d.xsrfHeaderName||e.xsrfHeaderName]=a);var h=[function(a){f=a.headers;var b=ic(a.data,hc(f),a.transformRequest);x(a.data)&&q(f,function(a,b){"content-type"===w(b)&&delete f[b]});x(a.withCredentials)&&!x(e.withCredentials)&&(a.withCredentials=e.withCredentials);return C(a,b,f).then(c,c)},s],g=n.when(d);for(q(A,function(a){(a.request||a.requestError)&&h.unshift(a.request,a.requestError);(a.response||a.responseError)&&h.push(a.response,a.responseError)});h.length;){a=h.shift(); default:return b}}function za(a,b){if("srcdoc"==b)return z.HTML;var c=pa(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return z.RESOURCE_URL}function ya(a,c,d,e,h){var k=b(d,!0);if(k){if("multiple"===e&&"select"===pa(a))throw ja("selmulti",ta(a));c.push({priority:100,compile:function(){return{pre:function(c,d,l){d=l.$$observers||(l.$$observers={});if(g.test(e))throw ja("nodomevents");if(k=b(l[e],!0,za(a,e),f[e]||h))l[e]=k(c),(d[e]||(d[e]=[])).$$inter=!0,(l.$$observers&&
var k=h.shift(),g=g.then(a,k)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,d)});return g};g.error=function(a){g.then(null,function(b){a(b.data,b.status,b.headers,d)});return g};return g}function C(b,c,f){function g(a,b,c){q&&(200<=a&&300>a?q.put(s,[a,b,gc(c)]):q.remove(s));k(b,a,c);d.$$phase||d.$apply()}function k(a,c,d){c=Math.max(c,0);(200<=c&&300>c?r.resolve:r.reject)({data:a,status:c,headers:hc(d),config:b})}function m(){var a=ab(p.pendingRequests,b);-1!==a&&p.pendingRequests.splice(a, l.$$observers[e].$$scope||c).$watch(k,function(a,b){"class"===e&&a!=b?l.$updateClass(a,b):l.$set(e,a)})}}}})}}function rb(a,b,c){var d=b[0],e=b.length,f=d.parentNode,h,g;if(a)for(h=0,g=a.length;h<g;h++)if(a[h]==d){a[h++]=c;g=h+e-1;for(var k=a.length;h<k;h++,g++)g<k?a[h]=a[g]:delete a[h];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=Y.createDocumentFragment();a.appendChild(d);G(c).data(G(d).data());la?(Eb=!0,la.cleanData([d])):delete G.cache[d[G.expando]];d=1;for(e=b.length;d<
1)}var r=n.defer(),C=r.promise,q,A,s=H(b.url,b.params);p.pendingRequests.push(b);C.then(m,m);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(q=T(b.cache)?b.cache:T(e.cache)?e.cache:I);if(q)if(A=q.get(s),z(A)){if(A.then)return A.then(m,m),A;J(A)?k(A[1],A[0],da(A[2])):k(A,200,{})}else q.put(s,C);x(A)&&a(b.method,s,c,g,f,b.timeout,b.withCredentials,b.responseType);return C}function H(a,b){if(!b)return a;var c=[];Jc(b,function(a,b){null===a||x(a)||(J(a)||(a=[a]),q(a,function(a){T(a)&&(a=ma(a)); e;d++)f=b[d],G(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function ga(a,b){return E(function(){return a.apply(null,arguments)},a,b)}function Dc(a,b,c,d,e,f){try{a(b,c,d,e,f)}catch(h){p(h,ta(c))}}var Ob=function(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};Ob.prototype={$normalize:va,$addClass:function(a){a&&0<a.length&&T.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&T.removeClass(this.$$element,
c.push(ua(b)+"="+ua(a))}))});return a+(-1==a.indexOf("?")?"?":"&")+c.join("&")}var I=c("$http"),A=[];q(f,function(a){A.unshift(D(a)?r.get(a):r.invoke(a))});q(g,function(a,b){var c=D(a)?r.get(a):r.invoke(a);A.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});p.pendingRequests=[];(function(a){q(arguments,function(a){p[a]=function(b,c){return p(u(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){p[a]= a)},$updateClass:function(a,b){var c=Ec(a,b);c&&c.length&&T.addClass(this.$$element,c);(c=Ec(b,a))&&c.length&&T.removeClass(this.$$element,c)},$set:function(a,b,c,d){var e=this.$$element[0],f=yc(e,a),h=Ue(e,a),e=a;f?(this.$$element.prop(a,b),d=f):h&&(this[h]=b,e=h);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=Db(a,"-"));f=pa(this.$$element);if("a"===f&&"href"===a||"img"===f&&"src"===a)this[a]=b=v(b,"src"===a);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,
function(b,c,d){return p(u(d||{},{method:a,url:b,data:c}))}})})("post","put");p.defaults=e;return p}]}function kd(){this.$get=["$browser","$window","$document",function(b,a,c){return ld(b,md,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function ld(b,a,c,d,e,f){function g(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;P?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= b));(a=this.$$observers)&&r(a[e],function(a){try{a(b)}catch(c){p(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);u.$evalAsync(function(){e.$$inter||b(c[a])});return function(){Ra(e,b)}}};var ma=b.startSymbol(),ha=b.endSymbol(),V="{{"==ma||"}}"==ha?Pa:function(a){return a.replace(/\{\{/g,ma).replace(/}}/g,ha)},U=/^ngAttr[A-Z]/;Q.$$addBindingInfo=h?function(a,b){var c=a.data("$binding")||[];L(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:w;
d;e.body.appendChild(c);return d}return function(e,m,k,l,n,r,p,C){function H(){A=-1;G&&G();v&&v.abort()}function I(a,d,e,h){var g=f||xa(m).protocol;Z&&c.cancel(Z);G=v=null;d="file"==g?e?200:404:d;a(1223==d?204:d,e,h);b.$$completeOutstandingRequest(t)}var A;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==w(e)){var s="_"+(d.counter++).toString(36);d[s]=function(a){d[s].data=a};var G=g(m.replace("JSON_CALLBACK","angular.callbacks."+s),function(){d[s].data?I(l,200,d[s].data):I(l,A||-2);delete d[s]})}else{var v= Q.$$addBindingClass=h?function(a){y(a,"ng-binding")}:w;Q.$$addScopeInfo=h?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:w;Q.$$addScopeClass=h?function(a,b){y(a,b?"ng-isolate-scope":"ng-scope")}:w;return Q}]}function va(b){return Wa(b.replace(af,""))}function Ec(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function re(){var b={},a=!1,c=/^(\S+)(\s+as\s+(\w+))?$/;
new a;v.open(e,m,!0);q(n,function(a,b){z(a)&&v.setRequestHeader(b,a)});v.onreadystatechange=function(){if(4==v.readyState){var a=v.getAllResponseHeaders();I(l,A||v.status,v.responseType?v.response:v.responseText,a)}};p&&(v.withCredentials=!0);C&&(v.responseType=C);v.send(k||null)}if(0<r)var Z=c(H,r);else r&&r.then&&r.then(H)}}function nd(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler", this.register=function(a,c){Ja(a,"controller");S(a)?E(b,a):b[a]=c};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(d,e){function f(a,b,c,d){if(!a||!S(a.$scope))throw K("$controller")("noscp",d,b);a.$scope[b]=c}return function(g,h,m,k){var n,p,l;m=!0===m;k&&C(k)&&(l=k);C(g)&&(k=g.match(c),p=k[1],l=l||k[3],g=b.hasOwnProperty(p)?b[p]:ic(h.$scope,p,!0)||(a?ic(e,p,!0):s),gb(g,p,!0));if(m)return m=function(){},m.prototype=(L(g)?g[g.length-1]:g).prototype,n=new m,l&&f(h,l,n,
"$sce",function(c,d,e){function f(f,k,l){for(var n,r,p=0,C=[],H=f.length,q=!1,A=[];p<H;)-1!=(n=f.indexOf(b,p))&&-1!=(r=f.indexOf(a,n+g))?(p!=n&&C.push(f.substring(p,n)),C.push(p=c(q=f.substring(n+g,r))),p.exp=q,p=r+h,q=!0):(p!=H&&C.push(f.substring(p)),p=H);(H=C.length)||(C.push(""),H=1);if(l&&1<C.length)throw jc("noconcat",f);if(!k||q)return A.length=H,p=function(a){try{for(var b=0,c=H,h;b<c;b++)"function"==typeof(h=C[b])&&(h=h(a),h=l?e.getTrusted(l,h):e.valueOf(h),null===h||x(h)?h="":"string"!= p||g.name),E(function(){d.invoke(g,n,h,p);return n},{instance:n,identifier:l});n=d.instantiate(g,h,p);l&&f(h,l,n,p||g.name);return n}}]}function se(){this.$get=["$window",function(b){return G(b.document)}]}function te(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Fc(b){var a={},c,d,e;if(!b)return a;r(b.split("\n"),function(b){e=b.indexOf(":");c=P(ba(b.substr(0,e)));d=ba(b.substr(e+1));c&&(a[c]=a[c]?a[c]+", "+d:d)});return a}function Gc(b){var a=S(b)?b:
typeof h&&(h=ma(h))),A[b]=h;return A.join("")}catch(g){a=jc("interr",f,g.toString()),d(a)}},p.exp=f,p.parts=C,p}var g=b.length,h=a.length;f.startSymbol=function(){return b};f.endSymbol=function(){return a};return f}]}function od(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,g,h,m){var k=a.setInterval,l=a.clearInterval,n=c.defer(),r=n.promise,p=0,C=z(m)&&!m;h=z(h)?h:0;r.then(null,null,d);r.$$intervalId=k(function(){n.notify(p++);0<h&&p>=h&&(n.resolve(p),l(r.$$intervalId),delete e[r.$$intervalId]); s;return function(c){a||(a=Fc(b));return c?a[P(c)]||null:a}}function Hc(b,a,c){if(D(c))return c(b,a);r(c,function(c){b=c(b,a)});return b}function we(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){C(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=dc(d)));return d}],transformRequest:[function(a){return S(a)&&"[object File]"!==Ga.call(a)&&"[object Blob]"!==Ga.call(a)?sa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},
C||b.$apply()},g);e[r.$$intervalId]=n;return r}var e={};d.cancel=function(a){return a&&a.$$intervalId in e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],!0):!1};return d}]}function pd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")", post:qa(d),put:qa(d),patch:qa(d)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},f=!1;this.useApplyAsync=function(a){return B(a)?(f=!!a,this):f};var g=this.interceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,p,l){function q(a){function b(a){var d=E({},a,{data:Hc(a.data,a.headers,c.transformResponse)});a=a.status;return 200<=a&&300>a?d:p.reject(d)}var c={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},
gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy", d=function(a){var b=e.headers,c=E({},a.headers),d,f,b=E({},b.common,b[P(a.method)]);a:for(d in b){a=P(d);for(f in c)if(P(f)===a)continue a;c[d]=b[d]}(function(a){var b;r(a,function(c,d){D(c)&&(b=c(),null!=b?a[d]=b:delete a[d])})})(c);return c}(a);E(c,a);c.headers=d;c.method=ib(c.method);var f=[function(a){d=a.headers;var c=Hc(a.data,Gc(d),a.transformRequest);F(c)&&r(d,function(a,b){"content-type"===P(b)&&delete d[b]});F(a.withCredentials)&&!F(e.withCredentials)&&(a.withCredentials=e.withCredentials);
mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function kc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=sb(b[a]);return b.join("/")}function lc(b,a){var c=xa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=U(c.port)||qd[c.protocol]||null}function mc(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=xa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=Ub(d.search);a.$$hash=decodeURIComponent(d.hash); return A(a,c,d).then(b,b)},s],h=p.when(c);for(r(z,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var g=f.shift(),h=h.then(a,g)}h.success=function(a){h.then(function(b){a(b.data,b.status,b.headers,c)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,c)});return h};return h}function A(c,g,k){function l(a,b,c,e){function h(){z(b,a,c,e)}O&&(200<=a&&
a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function la(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Wa(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Fb(b){return b.substr(0,Wa(b).lastIndexOf("/")+1)}function nc(b,a){this.$$html5=!0;a=a||"";var c=Fb(b);lc(b,this);this.$$parse=function(a){var b=la(c,a);if(!D(b))throw Gb("ipthprfx",a,c);mc(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Vb(this.$$search),b=this.$$hash? 300>a?O.put(W,[a,b,Fc(c),e]):O.remove(W));f?d.$applyAsync(h):(h(),d.$$phase||d.$apply())}function z(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?r.resolve:r.reject)({data:a,status:b,headers:Gc(d),config:c,statusText:e})}function A(){var a=q.pendingRequests.indexOf(c);-1!==a&&q.pendingRequests.splice(a,1)}var r=p.defer(),M=r.promise,O,H,W=u(c.url,c.params);q.pendingRequests.push(c);M.then(A,A);!c.cache&&!e.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(O=S(c.cache)?c.cache:S(e.cache)?e.cache:
"#"+sb(this.$$hash):"";this.$$url=kc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=la(b,d))!==s)return d=e,(e=la(a,e))!==s?c+(la("/",e)||e):b+d;if((e=la(c,d))!==s)return c+e;if(c==d+"/")return c}}function Hb(b,a){var c=Fb(b);lc(b,this);this.$$parse=function(d){var e=la(b,d)||la(c,d),e="#"==e.charAt(0)?la(a,e):this.$$html5?e:"";if(!D(e))throw Gb("ihshprfx",d,a);mc(e,this);this.$$compose()};this.$$compose=function(){var c=Vb(this.$$search), x);if(O)if(H=O.get(W),B(H)){if(H&&D(H.then))return H.then(A,A),H;L(H)?z(H[1],H[0],qa(H[2]),H[3]):z(H,200,{},"OK")}else O.put(W,M);F(H)&&((H=Ic(c.url)?b.cookies()[c.xsrfCookieName||e.xsrfCookieName]:s)&&(k[c.xsrfHeaderName||e.xsrfHeaderName]=H),a(c.method,W,g,l,k,c.timeout,c.withCredentials,c.responseType));return M}function u(a,b){if(!b)return a;var c=[];od(b,function(a,b){null===a||F(a)||(L(a)||(a=[a]),r(a,function(a){S(a)&&(a=fa(a)?a.toISOString():sa(a));c.push(Da(b)+"="+Da(a))}))});0<c.length&&
e=this.$$hash?"#"+sb(this.$$hash):"";this.$$url=kc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Wa(b)==Wa(a))return a}}function oc(b,a){this.$$html5=!0;Hb.apply(this,arguments);var c=Fb(b);this.$$rewrite=function(d){var e;if(b==Wa(d))return d;if(e=la(c,d))return b+a+e;if(c===d+"/")return c}}function hb(b){return function(){return this[b]}}function pc(b,a){return function(c){if(x(c))return this[b];this[b]=a(c);this.$$compose();return this}} (a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var x=c("$http"),z=[];r(g,function(a){z.unshift(C(a)?l.get(a):l.invoke(a))});q.pendingRequests=[];(function(a){r(arguments,function(a){q[a]=function(b,c){return q(E(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){q[a]=function(b,c,d){return q(E(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");q.defaults=e;return q}]}function bf(b){if(8>=X&&(!b.match(/^(get|post|head|put|delete|options)$/i)||
function rd(){var b="",a=!1;this.hashPrefix=function(a){return z(a)?(b=a,this):b};this.html5Mode=function(b){return z(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,m=d.baseHref(),k=d.url();a?(m=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(m||"/"),e=e.history?nc:oc):(m=Wa(k),e=Hb);h=new e(m,"#"+b);h.$$parse(h.$$rewrite(k));f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&& !t.XMLHttpRequest))return new t.ActiveXObject("Microsoft.XMLHTTP");if(t.XMLHttpRequest)return new t.XMLHttpRequest;throw K("$httpBackend")("noxhr");}function xe(){this.$get=["$browser","$window","$document",function(b,a,c){return cf(b,bf,b.defer,a.angular.callbacks,c[0])}]}function cf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);
2!=a.which){for(var b=y(a.target);"a"!==w(b[0].nodeName);)if(b[0]===f[0]||!(b=b.parent())[0])return;var e=b.prop("href"),g=h.$$rewrite(e);e&&(!b.attr("target")&&g&&!a.isDefaultPrevented())&&(a.preventDefault(),g!=d.url()&&(h.$$parse(g),c.$apply(),W.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=k&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$broadcast("$locationChangeStart",a,h.absUrl()).defaultPrevented?d.url(h.absUrl()):(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a); f=null;var g=-1,q="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),q=a.type,g="error"===a.type?404:200);c&&c(g,q)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,m,k,n,p,l,q){function A(){x=-1;T&&T();v&&v.abort()}function u(a,d,e,f,g){Q&&c.cancel(Q);T=v=null;0===d&&(d=e?200:"file"==Aa(h).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(w)}var x;b.$$incOutstandingRequestCount();h=h||b.url();
g(b)}),c.$$phase||c.$digest()))});var l=0;c.$watch(function(){var a=d.url(),b=h.$$replace;l&&a==h.absUrl()||(l++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),g(a))}));h.$$replace=!1;return l});return h}]}function sd(){var b=!0,a=this;this.debugEnabled=function(a){return z(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+ if("jsonp"==P(e)){var z="_"+(d.counter++).toString(36);d[z]=function(a){d[z].data=a;d[z].called=!0};var T=f(h.replace("JSON_CALLBACK","angular.callbacks."+z),z,function(a,b){u(k,a,d[z].data,"",b);d[z]=w})}else{var v=a(e);v.open(e,h,!0);r(n,function(a,b){B(a)&&v.setRequestHeader(b,a)});v.onreadystatechange=function(){if(v&&4==v.readyState){var a=null,b=null,c="";-1!==x&&(a=v.getAllResponseHeaders(),b="response"in v?v.response:v.responseText);-1===x&&10>X||(c=v.statusText);u(k,x||v.status,b,a,c)}};
a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||t;return e.apply?function(){var a=[];q(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function ha(b,a,c){if("string"!==typeof b&&"[object String]"!==Ka.apply(b))return b; l&&(v.withCredentials=!0);if(q)try{v.responseType=q}catch(y){if("json"!==q)throw y;}v.send(m||null)}if(0<p)var Q=c(A,p);else p&&D(p.then)&&p.then(A)}}function ue(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(a){return"\\\\\\"+a}function g(f,g,q,A){function u(c){return c.replace(k,b).replace(n,a)}function x(a){try{var b;var c=q?e.getTrusted(q,a):e.valueOf(a);
if("constructor"===b&&!c)throw pa("isecfld",a);if("_"===b.charAt(0)||"_"===b.charAt(b.length-1))throw pa("isecprv",a);return b}function Xa(b,a){if(b&&b.constructor===b)throw pa("isecfn",a);if(b&&b.document&&b.location&&b.alert&&b.setInterval)throw pa("isecwindow",a);if(b&&(b.nodeName||b.on&&b.find))throw pa("isecdom",a);return b}function ib(b,a,c,d,e){e=e||{};a=a.split(".");for(var f,g=0;1<a.length;g++){f=ha(a.shift(),d);var h=b[f];h||(h={},b[f]=h);b=h;b.then&&e.unwrapPromises&&(qa(d),"$$v"in b|| if(null==c)b="";else{switch(typeof c){case "string":break;case "number":c=""+c;break;default:c=sa(c)}b=c}return b}catch(h){a=Pb("interr",f,h.toString()),d(a)}}A=!!A;for(var z,T,v=0,r=[],Q=[],s=f.length,J=[],N=[];v<s;)if(-1!=(z=f.indexOf(b,v))&&-1!=(T=f.indexOf(a,z+h)))v!==z&&J.push(u(f.substring(v,z))),v=f.substring(z+h,T),r.push(v),Q.push(c(v,x)),v=T+m,N.push(J.length),J.push("");else{v!==s&&J.push(u(f.substring(v)));break}if(q&&1<J.length)throw Pb("noconcat",f);if(!g||r.length){var M=function(a){for(var b=
function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}f=ha(a.shift(),d);return b[f]=c}function qc(b,a,c,d,e,f,g){ha(b,f);ha(a,f);ha(c,f);ha(d,f);ha(e,f);return g.unwrapPromises?function(h,g){var k=g&&g.hasOwnProperty(b)?g:h,l;if(null===k||k===s)return k;(k=k[b])&&k.then&&(qa(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!a||null===k||k===s)return k;(k=k[a])&&k.then&&(qa(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!c||null===k|| 0,c=r.length;b<c;b++){if(A&&F(a[b]))return;J[N[b]]=a[b]}return J.join("")};return E(function(a){var b=0,c=r.length,e=Array(c);try{for(;b<c;b++)e[b]=Q[b](a);return M(e)}catch(h){a=Pb("interr",f,h.toString()),d(a)}},{exp:f,expressions:r,$$watchDelegate:function(a,b,c){var d;return a.$watchGroup(Q,function(c,e){var f=M(c);D(b)&&b.call(this,f,c!==e?d:f,a);d=f},c)}})}}var h=b.length,m=a.length,k=new RegExp(b.replace(/./g,f),"g"),n=new RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol=
k===s)return k;(k=k[c])&&k.then&&(qa(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!d||null===k||k===s)return k;(k=k[d])&&k.then&&(qa(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!e||null===k||k===s)return k;(k=k[e])&&k.then&&(qa(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);return k}:function(f,g){var k=g&&g.hasOwnProperty(b)?g:f;if(null===k||k===s)return k;k=k[b];if(!a||null===k||k===s)return k;k=k[a];if(!c||null===k||k===s)return k; function(){return a};return g}]}function ve(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,h,m,k){var n=a.setInterval,p=a.clearInterval,l=0,q=B(k)&&!k,A=(q?d:c).defer(),u=A.promise;m=B(m)?m:0;u.then(null,null,e);u.$$intervalId=n(function(){A.notify(l++);0<m&&l>=m&&(A.resolve(l),p(u.$$intervalId),delete f[u.$$intervalId]);q||b.$apply()},h);f[u.$$intervalId]=A;return u}var f={};e.cancel=function(b){return b&&b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),
k=k[c];if(!d||null===k||k===s)return k;k=k[d];return e&&null!==k&&k!==s?k=k[e]:k}}function rc(b,a,c){if(Ib.hasOwnProperty(b))return Ib[b];var d=b.split("."),e=d.length,f;if(a.csp)f=6>e?qc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var h=0,g;do g=qc(d[h++],d[h++],d[h++],d[h++],d[h++],c,a)(b,f),f=s,b=g;while(h<e);return g};else{var g="var l, fn, p;\n";q(d,function(b,d){ha(b,c);g+="if(s === null || s === undefined) return s;\nl=s;\ns="+(d?"s":'((k&&k.hasOwnProperty("'+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises? delete f[b.$$intervalId],!0):!1};return e}]}function Dd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
'if (s && s.then) {\n pw("'+c.replace(/\"/g,'\\"')+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});var g=g+"return s;",h=new Function("s","k","pw",g);h.toString=function(){return g};f=function(a,b){return h(a,b,qa)}}"hasOwnProperty"!==b&&(Ib[b]=f);return f}function td(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=function(b){return z(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings= DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a",short:"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Qb(b){b=b.split("/");for(var a=b.length;a--;)b[a]=eb(b[a]);return b.join("/")}function Jc(b,a,c){b=Aa(b,c);a.$$protocol=
function(b){return z(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(c,d,e){a.csp=d.csp;qa=function(b){a.logPromiseWarnings&&!sc.hasOwnProperty(b)&&(sc[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};return function(d){var e;switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];e=new Jb(a);e=(new Ya(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&& b.protocol;a.$$host=b.hostname;a.$$port=U(b.port)||df[b.protocol]||null}function Kc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=Aa(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=fc(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function wa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ya(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Rb(b){return b.substr(0,
(b[d]=e);return e;case "function":return d;default:return t}}}]}function ud(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return vd(function(a){b.$evalAsync(a)},a)}]}function vd(b,a){function c(a){return a}function d(a){return g(a)}var e=function(){var h=[],m,k;return k={resolve:function(a){if(h){var c=h;h=s;m=f(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],m.then(a[0],a[1],a[2])})}},reject:function(a){k.resolve(g(a))},notify:function(a){if(h){var c=h;h.length&&b(function(){for(var b, Ya(b).lastIndexOf("/")+1)}function Lc(b,a){this.$$html5=!0;a=a||"";var c=Rb(b);Jc(b,this,b);this.$$parse=function(a){var e=wa(c,a);if(!C(e))throw sb("ipthprfx",a,c);Kc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Bb(this.$$search),b=this.$$hash?"#"+eb(this.$$hash):"";this.$$url=Qb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=wa(b,d))!==s?
d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,f,g){var k=e(),C=function(d){try{k.resolve((B(b)?b:c)(d))}catch(e){k.reject(e),a(e)}},H=function(b){try{k.resolve((B(f)?f:d)(b))}catch(c){k.reject(c),a(c)}},q=function(b){try{k.notify((B(g)?g:c)(b))}catch(d){a(d)}};h?h.push([C,H,q]):m.then(C,H,q);return k.promise},"catch":function(a){return this.then(null,a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,f){var h=null;try{h= (g=f,g=(f=wa(a,f))!==s?c+(wa("/",f)||f):b+g):(f=wa(c,d))!==s?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function Sb(b,a){var c=Rb(b);Jc(b,this,b);this.$$parse=function(d){var e=wa(b,d)||wa(c,d),e="#"==e.charAt(0)?wa(a,e):this.$$html5?e:"";if(!C(e))throw sb("ihshprfx",d,a);Kc(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Bb(this.$$search),e=this.$$hash?
(a||c)()}catch(g){return b(g,!1)}return h&&B(h.then)?h.then(function(){return b(e,f)},function(a){return b(a,!1)}):b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},f=function(a){return a&&B(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},g=function(c){return{then:function(f,g){var l=e();b(function(){try{l.resolve((B(g)?g:d)(c))}catch(b){l.reject(b),a(b)}});return l.promise}}};return{defer:e,reject:g,when:function(h,m,k,l){var n= "#"+eb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ya(b)==Ya(a)?(this.$$parse(a),!0):!1}}function Mc(b,a){this.$$html5=!0;Sb.apply(this,arguments);var c=Rb(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ya(d)?f=d:(g=wa(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);return!!f};this.$$compose=function(){var c=Bb(this.$$search),e=this.$$hash?"#"+eb(this.$$hash):
e(),r,p=function(b){try{return(B(m)?m:c)(b)}catch(d){return a(d),g(d)}},C=function(b){try{return(B(k)?k:d)(b)}catch(c){return a(c),g(c)}},q=function(b){try{return(B(l)?l:c)(b)}catch(d){a(d)}};b(function(){f(h).then(function(a){r||(r=!0,n.resolve(f(a).then(p,C,q)))},function(a){r||(r=!0,n.resolve(C(a)))},function(a){r||n.notify(q(a))})});return n.promise},all:function(a){var b=e(),c=0,d=J(a)?[]:{};q(a,function(a,e){c++;f(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)|| "";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function tb(b){return function(){return this[b]}}function Nc(b,a){return function(c){if(F(c))return this[b];this[b]=a(c);this.$$compose();return this}}function ye(){var b="",a=!1;this.hashPrefix=function(a){return B(a)?(b=a,this):b};this.html5Mode=function(b){return B(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),
b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function wd(){var b=10,a=L("$rootScope");this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(c,d,e,f){function g(){this.$id=Za();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$isolateBindings= a)}var h,m=d.baseHref(),k=d.url();if(a){if(!m)throw sb("nobase");m=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(m||"/");e=e.history?Lc:Mc}else m=Ya(k),e=Sb;h=new e(m,"#"+b);h.$$parseLinkUrl(k,k);var n=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=G(a.target);"a"!==pa(b[0]);)if(b[0]===f[0]||!(b=b.parent())[0])return;var e=b.prop("href"),g=b.attr("href")||b.attr("xlink:href");S(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=Aa(e.animVal).href);
{}}function h(b){if(l.$$phase)throw a("inprog",l.$$phase);l.$$phase=b}function m(a,b){var c=e(a);Oa(c,b);return c}function k(){}g.prototype={constructor:g,$new:function(a){a?(a=new g,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=this.$$postDigestQueue):(a=function(){},a.prototype=this,a=new a,a.$id=Za());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail= n.test(e)||!e||b.attr("target")||a.isDefaultPrevented()||!h.$$parseLinkUrl(e,g)||(a.preventDefault(),h.absUrl()!=d.url()&&(c.$apply(),t.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=k&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):g(b)}),c.$$phase||c.$digest())});var p=0;c.$watch(function(){var a=d.url(),b=h.$$replace;p&&a==h.absUrl()||
this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,c){var d=m(a,"watch"),e=this.$$watchers,f={fn:b,last:k,get:d,exp:a,eq:!!c};if(!B(b)){var h=m(b||t,"listener");f.fn=function(a,b,c){h(c)}}if("string"==typeof a&&d.constant){var g=f.fn;f.fn=function(a,b,c){g.call(this,a,b,c);La(e,f)}}e||(e=this.$$watchers=[]);e.unshift(f);return function(){La(e,f)}},$watchCollection:function(a,b){var c=this,d,f,h=0,g=e(a),k=[],l={},m=0;return this.$watch(function(){f= (p++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),g(a))}));h.$$replace=!1;return p});return h}]}function ze(){var b=!0,a=this;this.debugEnabled=function(a){return B(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=
g(c);var a,b;if(T(f))if(pb(f))for(d!==k&&(d=k,m=d.length=0,h++),a=f.length,m!==a&&(h++,d.length=m=a),b=0;b<a;b++)d[b]!==f[b]&&(h++,d[b]=f[b]);else{d!==l&&(d=l={},m=0,h++);a=0;for(b in f)f.hasOwnProperty(b)&&(a++,d.hasOwnProperty(b)?d[b]!==f[b]&&(h++,d[b]=f[b]):(m++,d[b]=f[b],h++));if(m>a)for(b in h++,d)d.hasOwnProperty(b)&&!f.hasOwnProperty(b)&&(m--,delete d[b])}else d!==f&&(d=f,h++);return h},function(){b(f,d,c)})},$digest:function(){var c,e,f,g,m=this.$$asyncQueue,q=this.$$postDigestQueue,s,t,G= c.console||{},e=b[a]||b.log||w;a=!1;try{a=!!e.apply}catch(m){}return a?function(){var a=[];r(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function na(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw oa("isecfld",a);return b}function Ba(b,
b,v,y=[],E,z,$;h("$digest");do{t=!1;for(v=this;m.length;)try{$=m.shift(),$.scope.$eval($.expression)}catch(x){d(x)}do{if(g=v.$$watchers)for(s=g.length;s--;)try{(c=g[s])&&((e=c.get(v))!==(f=c.last)&&!(c.eq?Aa(e,f):"number"==typeof e&&"number"==typeof f&&isNaN(e)&&isNaN(f)))&&(t=!0,c.last=c.eq?da(e):e,c.fn(e,f===k?e:f,v),5>G&&(E=4-G,y[E]||(y[E]=[]),z=B(c.exp)?"fn: "+(c.exp.name||c.exp.toString()):c.exp,z+="; newVal: "+ma(e)+"; oldVal: "+ma(f),y[E].push(z)))}catch(M){d(M)}if(!(g=v.$$childHead||v!==this&& a){if(b){if(b.constructor===b)throw oa("isecfn",a);if(b.window===b)throw oa("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw oa("isecdom",a);if(b===Object)throw oa("isecobj",a);}return b}function ub(b,a,c,d){Ba(b,d);a=a.split(".");for(var e,f=0;1<a.length;f++){e=na(a.shift(),d);var g=Ba(b[e],d);g||(g={},b[e]=g);b=g}e=na(a.shift(),d);Ba(b[e],d);return b[e]=c}function Oc(b,a,c,d,e,f){na(b,f);na(a,f);na(c,f);na(d,f);na(e,f);return function(f,h){var m=h&&h.hasOwnProperty(b)?h:
v.$$nextSibling))for(;v!==this&&!(g=v.$$nextSibling);)v=v.$parent}while(v=g);if(t&&!G--)throw l.$$phase=null,a("infdig",b,ma(y));}while(t||m.length);for(l.$$phase=null;q.length;)try{q.shift()()}catch(w){d(w)}},$destroy:function(){if(l!=this&&!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling); f;if(null==m)return m;m=m[b];if(!a)return m;if(null==m)return s;m=m[a];if(!c)return m;if(null==m)return s;m=m[c];if(!d)return m;if(null==m)return s;m=m[d];return e?null==m?s:m=m[e]:m}}function Pc(b,a,c){var d=Qc[b];if(d)return d;var e=b.split("."),f=e.length;if(a.csp)d=6>f?Oc(e[0],e[1],e[2],e[3],e[4],c):function(a,b){var d=0,g;do g=Oc(e[d++],e[d++],e[d++],e[d++],e[d++],c)(a,b),b=s,a=g;while(d<f);return g};else{var g="";r(e,function(a,b){na(a,c);g+="if(s == null) return undefined;\ns="+(b?"s":'((l&&l.hasOwnProperty("'+
this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null}},$eval:function(a,b){return e(a)(this,b)},$evalAsync:function(a){l.$$phase||l.$$asyncQueue.length||f.defer(function(){l.$$asyncQueue.length&&l.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return h("$apply"),this.$eval(a)}catch(b){d(b)}finally{l.$$phase= a+'"))?l:s)')+"."+a+";\n"});g+="return s;";a=new Function("s","l",g);a.toString=da(g);a.assign=function(a,c){return ub(a,b,c,b)};d=a}d.sharedGetter=!0;return Qc[b]=d}function Ae(){var b=Object.create(null),a={csp:!1};this.$get=["$filter","$sniffer",function(c,d){function e(a){var b=a;a.sharedGetter&&(b=function(b,c){return a(b,c)},b.literal=a.literal,b.constant=a.constant,b.assign=a.assign);return b}function f(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;D(b)&&b.apply(this,
null;try{l.$digest()}catch(c){throw d(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[ab(c,b)]=null}},$emit:function(a,b){var c=[],e,f=this,h=!1,g={name:a,targetScope:f,stopPropagation:function(){h=!0},preventDefault:function(){g.defaultPrevented=!0},defaultPrevented:!1},k=[g].concat(ta.call(arguments,1)),l,m;do{e=f.$$listeners[a]||c;g.currentScope=f;l=0;for(m=e.length;l<m;l++)if(e[l])try{e[l].apply(null,k)}catch(q){d(q)}else e.splice(l, arguments);B(a)&&d.$$postDigest(function(){B(f)&&e()})},c)}function g(a,b,c,d){function e(a){var b=!0;r(a,function(a){B(a)||(b=!1)});return b}var f;return f=a.$watch(function(a){return d(a)},function(a,c,d){D(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(a)&&f()})},c)}function h(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){D(b)&&b.apply(this,arguments);e()},c)}function m(a,b){if(!b)return a;var c=function(c,d){var e=a(c,d),f=b(e,c,d);return B(e)?f:e};c.$$watchDelegate=
1),l--,m--;if(h)break;f=f.$parent}while(f);return g},$broadcast:function(a,b){var c=this,e=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},h=[f].concat(ta.call(arguments,1)),g,k;do{c=e;f.currentScope=c;e=c.$$listeners[a]||[];g=0;for(k=e.length;g<k;g++)if(e[g])try{e[g].apply(null,h)}catch(l){d(l)}else e.splice(g,1),g--,k--;if(!(e=c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(e=c.$$nextSibling);)c=c.$parent}while(c=e);return f}};var l= a.$$watchDelegate;return c}a.csp=d.csp;return function(d,n){var p,l,q;switch(typeof d){case "string":return q=d=d.trim(),p=b[q],p||(":"===d.charAt(0)&&":"===d.charAt(1)&&(l=!0,d=d.substring(2)),p=new Tb(a),p=(new Za(p,c,a)).parse(d),p.constant?p.$$watchDelegate=h:l&&(p=e(p),p.$$watchDelegate=p.literal?g:f),b[q]=p),m(p,n);case "function":return m(d,n);default:return m(w,n)}}}]}function Ce(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Rc(function(a){b.$evalAsync(a)},a)}]}function De(){this.$get=
new g;return l}]}function xd(b){if("self"===b)return b;if(D(b)){if(-1<b.indexOf("***"))throw ra("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+b+"$")}if($a(b))return RegExp("^"+b.source+"$");throw ra("imatcher");}function tc(b){var a=[];z(b)&&q(b,function(b){a.push(xd(b))});return a}function yd(){this.SCE_CONTEXTS=ca;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&& ["$browser","$exceptionHandler",function(b,a){return Rc(function(a){b.defer(a)},a)}]}function Rc(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending;c.processScheduled=!1;c.pending=s;for(var f=0,h=e.length;f<h;++f){d=e[f][0];b=e[f][c.status];try{D(b)?d.resolve(b(c.value)):
(b=tc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=tc(b));return a};this.$get=["$log","$document","$injector",function(c,d,e){function f(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var g=function(a){throw ra("unsafe");};e.has("$sanitize")&&(g=e.get("$sanitize"));var h=f(), 1===c.status?d.resolve(c.value):d.reject(c.value)}catch(g){d.reject(g),a(g)}}}))}function g(){this.promise=new d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=K("$q",TypeError);d.prototype={then:function(a,b,c){var d=new g;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,
m={};m[ca.HTML]=f(h);m[ca.CSS]=f(h);m[ca.URL]=f(h);m[ca.JS]=f(h);m[ca.RESOURCE_URL]=f(m[ca.URL]);return{trustAs:function(a,b){var c=m.hasOwnProperty(a)?m[a]:null;if(!c)throw ra("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw ra("itype",a);return new c(b)},getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var e=m.hasOwnProperty(c)?m[c]:null;if(e&&d instanceof e)return d.$$unwrapTrustedValue();if(c===ca.RESOURCE_URL){var e=xa(d.toString()),f,h,q=!1;f=0;for(h= !0,a)},function(b){return k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(S(b)||D(b))d=b&&b.then;D(d)?(this.promise.$$state.status=-1,d.call(b,e[0],e[1],this.notify)):(this.promise.$$state.value=b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(h){e[1](h),a(h)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},
b.length;f<h;f++)if("self"===b[f]?Eb(e):b[f].exec(e.href)){q=!0;break}if(q)for(f=0,h=a.length;f<h;f++)if("self"===a[f]?Eb(e):a[f].exec(e.href)){q=!1;break}if(q)return d;throw ra("insecurl",d.toString());}if(c===ca.HTML)return g(d);throw ra("unsafe");},valueOf:function(a){return a instanceof h?a.$$unwrapTrustedValue():a}}}]}function zd(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$document","$sceDelegate",function(a,c,d){if(b&&P&&(c=c[0].documentMode, $$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b,e,f=0,h=d.length;f<h;f++){e=d[f][0];b=d[f][3];try{e.notify(D(b)?b(c):c)}catch(g){a(g)}}})}};var m=function(a,b){var c=new g;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{D(c)&&(d=c())}catch(e){return m(e,!1)}return d&&D(d.then)?d.then(function(){return m(a,
c!==s&&8>c))throw ra("iequirks");var e=da(ca);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=za);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,h=e.trustAs;q(ca,function(a,b){var c=w(b);e[Pa("parse_as_"+c)]=function(b){return f(a,b)};e[Pa("get_trusted_"+c)]=function(b){return g(a,b)};e[Pa("trust_as_"+ b)},function(a){return m(a,!1)}):m(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},p=function q(a){if(!D(a))throw h("norslvr",a);if(!(this instanceof q))return new q(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};p.defer=function(){return new g};p.reject=function(a){var b=new g;b.reject(a);return b.promise};p.when=n;p.all=function(a){var b=new g,c=0,d=L(a)?[]:{};r(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||
c)]=function(b){return h(a,b)}});return e}]}function Ad(){this.$get=["$window","$document",function(b,a){var c={},d=U((/android (\d+)/.exec(w((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|O|ms)(?=[A-Z])/,m=f.body&&f.body.style,k=!1,l=!1;if(m){for(var n in m)if(k=h.exec(n)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in m&&"webkit");k=!!("transition"in m||g+"Transition"in m);l=!!("animation"in m||g+"Animation"in (d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return p}function Me(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};
m);!d||k&&l||(k=D(f.body.style.webkitTransition),l=D(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!f.documentMode||7<f.documentMode),hasEvent:function(a){if("input"==a&&9==P)return!1;if(x(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Rb(),vendorPrefix:g,transitions:k,animations:l,msie:P}}]}function Bd(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,m){var k= f.supported=e;return f}]}function Be(){var b=10,a=K("$rootScope"),c=null,d=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(e,f,g,h){function m(){this.$id=++bb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$listenerCount=
c.defer(),l=k.promise,n=z(m)&&!m;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||b.$apply()},h);l.$$timeoutId=h;f[h]=k;return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function xa(b){P&&(R.setAttribute("href",b),b=R.href);R.setAttribute("href",b);return{href:R.href,protocol:R.protocol?R.protocol.replace(/:$/,""):"",host:R.host, {};this.$$isolateBindings={};this.$$applyAsyncQueue=[]}function k(b){if(A.$$phase)throw a("inprog",A.$$phase);A.$$phase=b}function n(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function p(){}function l(){for(var a=A.$$applyAsyncQueue;a.length;)try{a.shift()()}catch(b){f(b)}d=null}function q(){null===d&&(d=h.defer(function(){A.$apply(l)}))}m.prototype={constructor:m,$new:function(a){a?(a=new m,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,
search:R.search?R.search.replace(/^\?/,""):"",hash:R.hash?R.hash.replace(/^#/,""):"",hostname:R.hostname,port:R.port,pathname:R.pathname&&"/"===R.pathname.charAt(0)?R.pathname:"/"+R.pathname}}function Eb(b){b=D(b)?xa(b):b;return b.protocol===uc.protocol&&b.host===uc.host}function Cd(){this.$get=aa(W)}function vc(b){function a(d,e){if(T(d)){var f={};q(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+ a.$$postDigestQueue=this.$$postDigestQueue):(this.$$ChildScope||(this.$$ChildScope=function(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$id=++bb;this.$$ChildScope=null},this.$$ChildScope.prototype=this),a=new this.$$ChildScope);a["this"]=a;a.$parent=this;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,
c)}}];a("currency",wc);a("date",xc);a("filter",Dd);a("json",Ed);a("limitTo",Fd);a("lowercase",Gd);a("number",yc);a("orderBy",zc);a("uppercase",Hd)}function Dd(){return function(b,a,c){if(!J(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return bb.equals(a,b)}:function(a,b){b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"==typeof b&&"!"=== d){var e=g(a);if(e.$$watchDelegate)return e.$$watchDelegate(this,b,d,e);var f=this.$$watchers,h={fn:b,last:p,get:e,exp:a,eq:!!d};c=null;D(b)||(h.fn=w);f||(f=this.$$watchers=[]);f.unshift(h);return function(){Ra(f,h);c=null}},$watchGroup:function(a,b){function c(){g=!1;k?(k=!1,b(e,e,h)):b(e,d,h)}var d=Array(a.length),e=Array(a.length),f=[],h=this,g=!1,k=!0;if(!a.length){var m=!0;h.$evalAsync(function(){m&&b(e,e,h)});return function(){m=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=
b.charAt(0))return!f(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var g in a)"$"==g?function(){if(a[g]){var b=g;e.push(function(c){return f(c,a[b])})}}(): a;d[0]=c;b(e,a===c?e:d,f)});r(a,function(a,b){var k=h.$watch(a,function(a,f){e[b]=a;d[b]=f;g||(g=!0,h.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){var c=this,d,e,f,h=1<b.length,k=0,m=g(a,function(a){d=a;var b,c,f,h;if(S(d))if(Na(d))for(e!==l&&(e=l,q=e.length=0,k++),a=d.length,q!==a&&(k++,e.length=q=a),b=0;b<a;b++)h=e[b],f=d[b],c=h!==h&&f!==f,c||h===f||(k++,e[b]=f);else{e!==n&&(e=n={},q=0,k++);a=0;for(b in d)d.hasOwnProperty(b)&&(a++,f=
function(){if("undefined"!=typeof a[g]){var b=g;e.push(function(c){return f(ub(c,b),a[b])})}}();break;case "function":e.push(a);break;default:return b}for(var d=[],h=0;h<b.length;h++){var m=b[h];e.check(m)&&d.push(m)}return d}}function wc(b){var a=b.NUMBER_FORMATS;return function(b,d){x(d)&&(d=a.CURRENCY_SYM);return Ac(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function yc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Ac(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}} d[b],h=e[b],b in e?(c=h!==h&&f!==f,c||h===f||(k++,e[b]=f)):(q++,e[b]=f,k++));if(q>a)for(b in k++,e)d.hasOwnProperty(b)||(q--,delete e[b])}else e!==d&&(e=d,k++);return k}),l=[],n={},p=!0,q=0;return this.$watch(m,function(){p?(p=!1,b(d,d,c)):b(d,f,c);if(h)if(S(d))if(Na(d)){f=Array(d.length);for(var a=0;a<d.length;a++)f[a]=d[a]}else for(a in f={},d)Ab.call(d,a)&&(f[a]=d[a]);else f=d})},$digest:function(){var e,g,m,n,q=this.$$asyncQueue,r=this.$$postDigestQueue,s,B,J=b,N,M=[],O,H,W;k("$digest");h.$$checkUrlChange();
function Ac(b,a,c,d,e){if(isNaN(b)||!isFinite(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",m=[],k=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&l[3]>e+1?g="0":(h=g,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));else{g=(g.split(Bc)[1]||"").length;x(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));g=Math.pow(10,e);b=Math.round(b*g)/g;b=(""+b).split(Bc);g=b[0];b=b[1]||"";var l=0,n=a.lgSize,r=a.gSize;if(g.length>=n+r)for(l=g.length-n,k=0;k<l;k++)0===(l-k)%r&&0!== this===A&&null!==d&&(h.defer.cancel(d),l());c=null;do{B=!1;for(N=this;q.length;){try{W=q.shift(),W.scope.$eval(W.expression)}catch(R){f(R)}c=null}a:do{if(n=N.$$watchers)for(s=n.length;s--;)try{if(e=n[s])if((g=e.get(N))!==(m=e.last)&&!(e.eq?ra(g,m):"number"===typeof g&&"number"===typeof m&&isNaN(g)&&isNaN(m)))B=!0,c=e,e.last=e.eq?Ha(g,null):g,e.fn(g,m===p?g:m,N),5>J&&(O=4-J,M[O]||(M[O]=[]),H=D(e.exp)?"fn: "+(e.exp.name||e.exp.toString()):e.exp,H+="; newVal: "+sa(g)+"; oldVal: "+sa(m),M[O].push(H));
k&&(h+=c),h+=g.charAt(k);for(k=l;k<g.length;k++)0===(g.length-k)%n&&0!==k&&(h+=c),h+=g.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}m.push(f?a.negPre:a.posPre);m.push(h);m.push(f?a.negSuf:a.posSuf);return m.join("")}function Kb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function V(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Kb(e,a,d)}}function jb(b,a){return function(c, else if(e===c){B=!1;break a}}catch(t){f(t)}if(!(n=N.$$childHead||N!==this&&N.$$nextSibling))for(;N!==this&&!(n=N.$$nextSibling);)N=N.$parent}while(N=n);if((B||q.length)&&!J--)throw A.$$phase=null,a("infdig",b,sa(M));}while(B||q.length);for(A.$$phase=null;r.length;)try{r.shift()()}catch(G){f(G)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(this!==A){for(var b in this.$$listenerCount)n(this,this.$$listenerCount[b],b);a.$$childHead==
d){var e=c["get"+b](),f=Ga(a?"SHORT"+b:b);return d[f][e]}}function xc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=U(b[9]+b[10]),g=U(b[9]+b[11]));h.call(a,U(b[1]),U(b[2])-1,U(b[3]));f=U(b[4]||0)-f;g=U(b[5]||0)-g;h=U(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; this&&(a.$$childHead=this.$$nextSibling);a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null;this.$$listeners={};this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[];this.$destroy=this.$digest=this.$apply=w;this.$on=this.$watch=this.$watchGroup=
return function(c,e){var f="",g=[],h,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;D(c)&&(c=Id.test(c)?U(c):a(c));qb(c)&&(c=new Date(c));if(!Ja(c))return c;for(;e;)(m=Jd.exec(e))?(g=g.concat(ta.call(m,1)),e=g.pop()):(g.push(e),e=null);q(g,function(a){h=Kd[a];f+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Ed(){return function(b){return ma(b,!0)}}function Fd(){return function(b,a){if(!J(b)&&!D(b))return b;a=U(a);if(D(b))return a?0<=a?b.slice(0,a):b.slice(a, function(){return w}}}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a){A.$$phase||A.$$asyncQueue.length||h.defer(function(){A.$$asyncQueue.length&&A.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return k("$apply"),this.$eval(a)}catch(b){f(b)}finally{A.$$phase=null;try{A.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&A.$$applyAsyncQueue.push(b);
b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function zc(b){return function(a,c,d){function e(a,b){return Na(b)?function(b,c){return a(c,b)}:a}if(!J(a)||!c)return a;c=J(c)?c:[c];c=Lc(c,function(a){var c=!1,d=a||za;if(D(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?("string"==f&&(c= q()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){c[c.indexOf(b)]=null;n(e,1,a)}},$emit:function(a,b){var c=[],d,e=this,h=!1,g={name:a,targetScope:e,stopPropagation:function(){h=!0},preventDefault:function(){g.defaultPrevented=!0},defaultPrevented:!1},k=db([g],arguments,1),m,l;do{d=e.$$listeners[a]||c;g.currentScope=e;m=0;for(l=
c.toLowerCase(),e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},c)});for(var f=[],g=0;g<a.length;g++)f.push(a[g]);return f.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function sa(b){B(b)&&(b={link:b});b.restrict=b.restrict||"AC";return aa(b)}function Cc(b,a){function c(a,c){c=c?"-"+cb(c,"-"):"";b.removeClass((a?kb:lb)+c).addClass((a?lb:kb)+c)}var d=this,e=b.parent().controller("form")||mb,f=0,g=d.$error={},h=[];d.$name=a.name||a.ngForm; d.length;m<l;m++)if(d[m])try{d[m].apply(null,k)}catch(n){f(n)}else d.splice(m,1),m--,l--;if(h)return g.currentScope=null,g;e=e.$parent}while(e);g.currentScope=null;return g},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var h=db([e],arguments,1),g,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];g=0;for(k=d.length;g<k;g++)if(d[g])try{d[g].apply(null,h)}catch(m){f(m)}else d.splice(g,
d.$dirty=!1;d.$pristine=!0;d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(Ha);c(!0);d.$addControl=function(a){na(a.$name,"input");h.push(a);a.$name&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&&d[a.$name]===a&&delete d[a.$name];q(g,function(b,c){d.$setValidity(c,!0,a)});La(h,a)};d.$setValidity=function(a,b,h){var n=g[a];if(b)n&&(La(n,h),n.length||(f--,f||(c(b),d.$valid=!0,d.$invalid=!1),g[a]=!1,c(!0,a),e.$setValidity(a,!0,d)));else{f||c(b);if(n){if(-1!=ab(n,h))return}else g[a]=n=[], 1),g--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var A=new m;return A}]}function Ed(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return B(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;if(!X||8<=X)if(f=
f++,c(!1,a),e.$setValidity(a,!1,d);n.push(h);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(Ha).addClass(nb);d.$dirty=!0;d.$pristine=!1;e.$setDirty()};d.$setPristine=function(){b.removeClass(nb).addClass(Ha);d.$dirty=!1;d.$pristine=!0;q(h,function(a){a.$setPristine()})}}function ob(b,a,c,d,e,f){var g=function(){var e=a.val();Na(c.ngTrim||"T")&&(e=Y(e));d.$viewValue!==e&&b.$apply(function(){d.$setViewValue(e)})};if(e.hasEvent("input"))a.on("input",g);else{var h,m=function(){h||(h= Aa(c).href,""!==f&&!f.match(e))return"unsafe:"+f;return c}}}function ef(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw Ca("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(cb(b))return new RegExp("^"+b.source+"$");throw Ca("imatcher");}function Sc(b){var a=[];B(b)&&r(b,function(b){a.push(ef(b))});return a}function Fe(){this.SCE_CONTEXTS=ka;var b=["self"],a=[];
f.defer(function(){g();h=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||m()});a.on("change",g);if(e.hasEvent("paste"))a.on("paste cut",m)}d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var k=c.ngPattern,l=function(a,b){if(d.$isEmpty(b)||a.test(b))return d.$setValidity("pattern",!0),b;d.$setValidity("pattern",!1);return s};k&&((e=k.match(/^\/(.*)\/([gim]*)$/))?(k=RegExp(e[1],e[2]),e=function(a){return l(k,a)}):e=function(c){var d=b.$eval(k); this.resourceUrlWhitelist=function(a){arguments.length&&(b=Sc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Sc(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?Ic(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}
if(!d||!d.test)throw L("ngPattern")("noregexp",k,d,ea(a));return l(d,c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var n=U(c.ngMinlength);e=function(a){if(!d.$isEmpty(a)&&a.length<n)return d.$setValidity("minlength",!1),s;d.$setValidity("minlength",!0);return a};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var r=U(c.ngMaxlength);e=function(a){if(!d.$isEmpty(a)&&a.length>r)return d.$setValidity("maxlength",!1),s;d.$setValidity("maxlength",!0);return a};d.$parsers.push(e); var f=function(a){throw Ca("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[ka.HTML]=e(g);h[ka.CSS]=e(g);h[ka.URL]=e(g);h[ka.JS]=e(g);h[ka.RESOURCE_URL]=e(h[ka.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ca("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw Ca("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===s||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();
d.$formatters.push(e)}}function Lb(b,a){b="ngClass"+b;return function(){return{restrict:"AC",link:function(c,d,e){function f(b){if(!0===a||c.$index%2===a)h&&!Aa(b,h)&&e.$removeClass(g(h)),e.$addClass(g(b));h=da(b)}function g(a){if(J(a))return a.join(" ");if(T(a)){var b=[];q(a,function(a,c){a&&b.push(c)});return b.join(" ")}return a}var h;c.$watch(e[b],f,!0);e.$observe("class",function(a){f(c.$eval(e[b]))});"ngClass"!==b&&c.$watch("$index",function(d,f){var h=d&1;h!==f&1&&(h===a?(h=c.$eval(e[b]),e.$addClass(g(h))): if(c===ka.RESOURCE_URL){var g=Aa(e.toString()),p,l,q=!1;p=0;for(l=b.length;p<l;p++)if(d(b[p],g)){q=!0;break}if(q)for(p=0,l=a.length;p<l;p++)if(d(a[p],g)){q=!1;break}if(q)return e;throw Ca("insecurl",e.toString());}if(c===ka.HTML)return f(e);throw Ca("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function Ee(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw Ca("iequirks");
(h=c.$eval(e[b]),e.$removeClass(g(h))))})}}}}var w=function(b){return D(b)?b.toLowerCase():b},Ga=function(b){return D(b)?b.toUpperCase():b},P,y,Ba,ta=[].slice,Ld=[].push,Ka=Object.prototype.toString,Ma=L("ng"),bb=W.angular||(W.angular={}),Ua,Ea,ia=["0","0","0"];P=U((/msie (\d+)/.exec(w(navigator.userAgent))||[])[1]);isNaN(P)&&(P=U((/trident\/.*; rv:(\d+)/.exec(w(navigator.userAgent))||[])[1]));t.$inject=[];za.$inject=[];var Y=function(){return String.prototype.trim?function(b){return D(b)?b.trim(): var e=qa(ka);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=Pa);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:a(c,function(a){return e.getTrusted(b,a)})};var f=e.parseAs,g=e.getTrusted,h=e.trustAs;r(ka,function(a,b){var c=P(b);e[Wa("parse_as_"+c)]=function(b){return f(a,b)};e[Wa("get_trusted_"+c)]=function(b){return g(a,b)};e[Wa("trust_as_"+c)]=function(b){return h(a,
b}:function(b){return D(b)?b.replace(/^\s*/,"").replace(/\s*$/,""):b}}();Ea=9>P?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ga(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Pc=/[A-Z]/g,Md={full:"1.2.0",major:1,minor:"NG_VERSION_MINOR",dot:0,codeName:"timely-delivery"},Ra=Q.cache={},db=Q.expando="ng-"+(new Date).getTime(),Tc=1,Dc=W.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+ b)}});return e}]}function Ge(){this.$get=["$window","$document",function(b,a){var c={},d=U((/android (\d+)/.exec(P((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g=f.documentMode,h,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=f.body&&f.body.style,n=!1,p=!1;if(k){for(var l in k)if(n=m.exec(l)){h=n[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");n=!!("transition"in k||h+"Transition"in k);p=!!("animation"in k||h+"Animation"in
a,c)},Ab=W.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},Rc=/([\:\-\_]+(.))/g,Sc=/^moz([A-Z])/,xb=L("jqLite"),Da=Q.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===O.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),Q(W).on("load",a))},toString:function(){var b=[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0, k);!d||n&&p||(n=C(f.body.style.webkitTransition),p=C(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!g||7<g),hasEvent:function(a){if("input"==a&&9==X)return!1;if(F(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Ua(),vendorPrefix:h,transitions:n,animations:p,android:d,msie:X,msieDocumentMode:g}}]}function Ie(){this.$get=["$templateCache","$http","$q",function(b,a,c){function d(e,f){function g(){h.totalPendingRequests--;
push:Ld,sort:[].sort,splice:[].splice},fb={};q("multiple selected checked disabled readOnly required open".split(" "),function(b){fb[w(b)]=b});var cc={};q("input select option textarea button form details".split(" "),function(b){cc[Ga(b)]=!0});q({data:$b,inheritedData:eb,scope:function(b){return y(b).data("$scope")||eb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return y(b).data("$isolateScope")||y(b).data("$isolateScopeNoTemplate")},controller:ac,injector:function(b){return eb(b, if(!f)throw ja("tpload",e);return c.reject()}var h=d;h.totalPendingRequests++;return a.get(e,{cache:b}).then(function(a){a=a.data;if(!a||0===a.length)return g();h.totalPendingRequests--;b.put(e,a);return a},g)}d.totalPendingRequests=0;return d}]}function Je(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var d=Ea.element(a).data("$binding");d&&r(d,function(d){c?(new RegExp("(^|\\s)"+
"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Bb,css:function(b,a,c){a=Pa(a);if(z(c))b.style[a]=c;else{var d;8>=P&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=P&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=w(a);if(fb[d])if(z(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||t).specified?d:s;else if(z(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a, b+"(\\s|\\||$)")).test(d)&&g.push(a):-1!=d.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,c){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var m=a.querySelectorAll("["+g[h]+"model"+(c?"=":"*=")+'"'+b+'"]');if(m.length)return m}},getLocation:function(){return c.url()},setLocation:function(a){a!==c.url()&&(c.url(a),b.$digest())},whenStable:function(b){a.notifyWhenNoOutstandingRequests(b)}}}]}function Ke(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,
2),null===b?s:b},prop:function(b,a,c){if(z(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(x(d))return e?b[e]:"";b[e]=d}var a=[];9>P?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(x(a)){if("SELECT"===Ea(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(x(a))return b.innerHTML;for(var c=0,d=b.childNodes;c< a,c,d,e){function f(f,m,k){var n=B(k)&&!k,p=(n?d:c).defer(),l=p.promise;m=a.defer(function(){try{p.resolve(f())}catch(a){p.reject(a),e(a)}finally{delete g[l.$$timeoutId]}n||b.$apply()},m);l.$$timeoutId=m;g[m]=p;return l}var g={};f.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return f}]}function Aa(b,a){var c=b;X&&(Z.setAttribute("href",c),c=Z.href);Z.setAttribute("href",c);return{href:Z.href,protocol:Z.protocol?
d.length;c++)Qa(d[c]);b.innerHTML=a}},function(b,a){Q.prototype[a]=function(a,d){var e,f;if((2==b.length&&b!==Bb&&b!==ac?a:d)===s){if(T(a)){for(e=0;e<this.length;e++)if(b===$b)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;f=e===s?Math.min(this.length,1):this.length;for(var g=0;g<f;g++){var h=b(this[g],a,d);e=e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:Yb,dealoc:Qa,on:function a(c,d,e,f){if(z(f))throw xb("onargs");var g=ja(c,"events"), Z.protocol.replace(/:$/,""):"",host:Z.host,search:Z.search?Z.search.replace(/^\?/,""):"",hash:Z.hash?Z.hash.replace(/^#/,""):"",hostname:Z.hostname,port:Z.port,pathname:"/"===Z.pathname.charAt(0)?Z.pathname:"/"+Z.pathname}}function Ic(b){b=C(b)?Aa(b):b;return b.protocol===Tc.protocol&&b.host===Tc.host}function Le(){this.$get=da(t)}function qc(b){function a(c,d){if(S(c)){var e={};r(c,function(b,c){e[c]=a(c,b)});return e}return b.factory(c+"Filter",d)}this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+
h=ja(c,"handle");g||ja(c,"events",g={});h||ja(c,"handle",h=Uc(c,g));q(d.split(" "),function(d){var f=g[d];if(!f){if("mouseenter"==d||"mouseleave"==d){var l=O.body.contains||O.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};g[d]=[];a(c,{mouseleave:"mouseout", "Filter")}}];a("currency",Uc);a("date",Vc);a("filter",ff);a("json",gf);a("limitTo",hf);a("lowercase",jf);a("number",Wc);a("orderBy",Xc);a("uppercase",kf)}function ff(){return function(b,a,c){if(!L(b))return b;var d=typeof c,e=[];e.check=function(a,b){for(var c=0;c<e.length;c++)if(!e[c](a,b))return!1;return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Ea.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&Ab.call(a,d)&&c(a[d],
mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||h(a,d)})}else Dc(c,d,h),g[d]=[];f=g[d]}f.push(e)})},off:Zb,replaceWith:function(a,c){var d,e=a.parentNode;Qa(a);q(new Q(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.childNodes||[]},append:function(a,c){q(new Q(c),function(c){1!==a.nodeType&&11!==a.nodeType|| b[d]))return!0;return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!f(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a=
a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new Q(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Qa(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new Q(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:Db,removeClass:Cb,toggleClass:function(a,c,d){x(d)&&(d=!Bb(a,c));(d?Db:Cb)(a,c)},parent:function(a){return(a= {$:a};case "object":for(var g in a)(function(b){"undefined"!==typeof a[b]&&e.push(function(c){return f("$"==b?c:c&&c[b],a[b])})})(g);break;case "function":e.push(a);break;default:return b}d=[];for(g=0;g<b.length;g++){var h=b[g];e.check(h,g)&&d.push(h)}return d}}function Uc(b){var a=b.NUMBER_FORMATS;return function(b,d){F(d)&&(d=a.CURRENCY_SYM);return null==b?b:Yc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Wc(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==
a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName(c)},clone:zb,triggerHandler:function(a,c,d){c=(ja(a,"events")||{})[c];d=d||[];var e=[{preventDefault:t,stopPropagation:t}];q(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){Q.prototype[c]=function(c,e,f){for(var g,h=0;h<this.length;h++)x(g)?(g=a(this[h],c,e,f),z(g)&& b?b:Yc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Yc(b,a,c,d,e){if(!isFinite(b)||S(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",m=[],k=!1;if(-1!==g.indexOf("e")){var n=g.match(/([\d\.]+)e(-?)(\d+)/);n&&"-"==n[2]&&n[3]>e+1?(g="0",b=0):(h=g,k=!0)}if(k)0<e&&-1<b&&1>b&&(h=b.toFixed(e));else{g=(g.split(Zc)[1]||"").length;F(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);0===b&&(f=!1);b=(""+b).split(Zc);g=b[0];b=b[1]||"";var n=
(g=y(g))):yb(g,a(this[h],c,e,f));return z(g)?g:this};Q.prototype.bind=Q.prototype.on;Q.prototype.unbind=Q.prototype.off});Sa.prototype={put:function(a,c){this[Ca(a)]=c},get:function(a){return this[Ca(a)]},remove:function(a){var c=this[a=Ca(a)];delete this[a];return c}};var Wc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Xc=/,/,Yc=/^\s*(_?)(\S+?)\1\s*$/,Vc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ta=L("$injector"),Nd=L("$animate"),Od=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e= 0,p=a.lgSize,l=a.gSize;if(g.length>=p+l)for(n=g.length-p,k=0;k<n;k++)0===(n-k)%l&&0!==k&&(h+=c),h+=g.charAt(k);for(k=n;k<g.length;k++)0===(g.length-k)%p&&0!==k&&(h+=c),h+=g.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}m.push(f?a.negPre:a.posPre);m.push(h);m.push(f?a.negSuf:a.posSuf);return m.join("")}function vb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function $(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();
c+"-animation";if(c&&"."!=c.charAt(0))throw Nd("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.$get=["$timeout",function(a){return{enter:function(d,e,f,g){f=f&&f[f.length-1];var h=e&&e[0]||f&&f.parentNode,m=f&&f.nextSibling||null;q(d,function(a){h.insertBefore(a,m)});g&&a(g,0,!1)},leave:function(d,e){d.remove();e&&a(e,0,!1)},move:function(a,c,f,g){this.enter(a,c,f,g)},addClass:function(d,e,f){e=D(e)?e:J(e)?e.join(" "):"";q(d,function(a){Db(a,e)});f&&a(f,0,!1)},removeClass:function(d, if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return vb(e,a,d)}}function wb(b,a){return function(c,d){var e=c["get"+b](),f=ib(a?"SHORT"+b:b);return d[f][e]}}function $c(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function ad(b){return function(a){var c=$c(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return vb(a,b)}}function Vc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:
e,f){e=D(e)?e:J(e)?e.join(" "):"";q(d,function(a){Cb(a,e)});f&&a(f,0,!1)},enabled:t}}]}],fa=L("$compile");ec.$inject=["$provide"];var fd=/^(x[\:\-_]|data[\:\-_])/i,md=W.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw L("$httpBackend")("noxhr");},jc=L("$interpolate"),Pd=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,qd={http:80,https:443,ftp:21},Gb= a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=U(b[9]+b[10]),g=U(b[9]+b[11]));h.call(a,U(b[1]),U(b[2])-1,U(b[3]));f=U(b[4]||0)-f;g=U(b[5]||0)-g;h=U(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e,f){var g="",h=[],m,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;C(c)&&(c=lf.test(c)?U(c):a(c));ea(c)&&(c=new Date(c));if(!fa(c))return c;
L("$location");oc.prototype=Hb.prototype=nc.prototype={$$html5:!1,$$replace:!1,absUrl:hb("$$absUrl"),url:function(a,c){if(x(a))return this.$$url;var d=Pd.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:hb("$$protocol"),host:hb("$$host"),port:hb("$$port"),path:pc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(D(a))this.$$search= for(;e;)(k=mf.exec(e))?(h=db(h,k,1),e=h.pop()):(h.push(e),e=null);f&&"UTC"===f&&(c=new Date(c.getTime()),c.setMinutes(c.getMinutes()+c.getTimezoneOffset()));r(h,function(a){m=nf[a];g+=m?m(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function gf(){return function(b){return sa(b,!0)}}function hf(){return function(b,a){if(!L(b)&&!C(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):U(a);if(C(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?
Ub(a);else if(T(a))this.$$search=a;else throw Gb("isrcharg");break;default:x(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:pc("$$hash",za),replace:function(){this.$$replace=!0;return this}};var pa=L("$parse"),sc={},qa,Ia={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:t,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return z(d)?z(e)?d+e:d:z(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(z(d)?d:0)-(z(e)? a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Xc(b){return function(a,c,d){function e(a,b){return b?function(b,c){return a(c,b)}:a}function f(a,b){var c=typeof a,d=typeof b;return c==d?(fa(a)&&fa(b)&&(a=a.valueOf(),b=b.valueOf()),"string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!Na(a)||!c)return a;c=L(c)?c:[c];c=sd(c,function(a){var c=!1,d=a||Pa;if(C(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c=
e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":t,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<= "-"==a.charAt(0),a=a.substring(1);d=b(a);if(d.constant){var h=d();return e(function(a,b){return f(a[h],b[h])},c)}}return e(function(a,b){return f(d(a),d(b))},c)});for(var g=[],h=0;h<a.length;h++)g.push(a[h]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function Fa(b){D(b)&&(b={link:b});b.restrict=b.restrict||"AC";return da(b)}function bd(b,a,c,d){var e=this,f=b.parent().controller("form")||xb,g=[];e.$error={};e.$$success={};e.$pending=
e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Qd={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Jb=function(a){this.options=a};Jb.prototype={constructor:Jb,lex:function(a){this.text=a;this.index=0;this.ch=s;this.lastCh=":";this.tokens=[];var c;for(a=[];this.index<this.text.length;){this.ch= s;e.$name=a.name||a.ngForm;e.$dirty=!1;e.$pristine=!0;e.$valid=!0;e.$invalid=!1;e.$submitted=!1;f.$addControl(e);b.addClass(Ma);e.$rollbackViewValue=function(){r(g,function(a){a.$rollbackViewValue()})};e.$commitViewValue=function(){r(g,function(a){a.$commitViewValue()})};e.$addControl=function(a){Ja(a.$name,"input");g.push(a);a.$name&&(e[a.$name]=a)};e.$removeControl=function(a){a.$name&&e[a.$name]===a&&delete e[a.$name];r(e.$pending,function(b,c){e.$setValidity(c,null,a)});r(e.$error,function(b,
this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent(),this.was("{,")&&("{"===a[0]&&(c=this.tokens[this.tokens.length-1]))&&(c.json=-1===c.text.indexOf("."));else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch,json:this.was(":[,")&&this.is("{[")||this.is("}]:,")}),this.is("{[")&&a.unshift(this.ch),this.is("}]")&&a.shift(), c){e.$setValidity(c,null,a)});Ra(g,a)};cd({ctrl:this,$element:b,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Ra(d,c),0===d.length&&delete a[b])},parentForm:f,$animate:d});e.$setDirty=function(){d.removeClass(b,Ma);d.addClass(b,yb);e.$dirty=!0;e.$pristine=!1;f.$setDirty()};e.$setPristine=function(){d.setClass(b,Ma,yb+" ng-submitted");e.$dirty=!1;e.$pristine=!0;e.$submitted=!1;r(g,function(a){a.$setPristine()})};e.$setSubmitted=function(){d.addClass(b,
this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{var d=this.ch+this.peek(),e=d+this.peek(2),f=Ia[this.ch],g=Ia[d],h=Ia[e];h?(this.tokens.push({index:this.index,text:e,fn:h}),this.index+=3):g?(this.tokens.push({index:this.index,text:d,fn:g}),this.index+=2):f?(this.tokens.push({index:this.index,text:this.ch,fn:f,json:this.was("[,:")&&this.is("+-")}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens}, "ng-submitted");e.$submitted=!0;f.$setSubmitted()}}function Ub(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function $a(b,a,c,d,e,f){a.prop("validity");var g=a[0].placeholder,h={},m=P(a[0].type);if(!e.android){var k=!1;a.on("compositionstart",function(a){k=!0});a.on("compositionend",function(){k=!1;n()})}var n=function(b){if(!k){var e=a.val(),f=b&&b.type;X&&"input"===(b||h).type&&a[0].placeholder!==g?g=a[0].placeholder:("password"===m||c.ngTrim&&"false"===c.ngTrim||(e=ba(e)),
is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d= (d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,f))}};if(e.hasEvent("input"))a.on("input",n);else{var p,l=function(a){p||(p=f.defer(function(){n(a);p=null}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||l(a)});if(e.hasEvent("paste"))a.on("paste cut",l)}a.on("change",n);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)}}function zb(b,a){return function(c){var d;if(fa(c))return c;if(C(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-
d||this.index;c=z(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw pa("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=w(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*= 1)&&(c=c.substring(1,c.length-1));if(of.test(c))return new Date(c);b.lastIndex=0;if(c=b.exec(c))return c.shift(),d={yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0},r(c,function(b,c){c<a.length&&(d[a[c]]=+b)}),new Date(d.yyyy,d.MM-1,d.dd,d.HH,d.mm,d.ss||0)}return NaN}}function ab(b,a,c,d){return function(e,f,g,h,m,k,n){function p(a){return B(a)?fa(a)?a:c(a):s}dd(e,f,g,h);$a(e,f,g,h,m,k);var l=h&&h.$options&&h.$options.timezone;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=
1;this.tokens.push({index:c,text:a,json:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,f,g,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;this.index++}if(e)for(f=this.index;f<this.text.length;){h=this.text.charAt(f);if("("===h){g=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(h))f++;else break}d={index:d,text:c};if(Ia.hasOwnProperty(c))d.fn= c(b),"UTC"===l&&b.setMinutes(b.getMinutes()-b.getTimezoneOffset()),b):s});h.$formatters.push(function(a){return fa(a)?n("date")(a,d,l):""});if(B(g.min)||g.ngMin){var q;h.$validators.min=function(a){return h.$isEmpty(a)||F(q)||c(a)>=q};g.$observe("min",function(a){q=p(a);h.$validate()})}if(B(g.max)||g.ngMax){var r;h.$validators.max=function(a){return h.$isEmpty(a)||F(r)||c(a)<=r};g.$observe("max",function(a){r=p(a);h.$validate()})}}}function dd(b,a,c,d){(d.$$hasNativeValidators=S(a[0].validity))&&
Ia[c],d.json=Ia[c];else{var m=rc(c,this.options,this.text);d.fn=u(function(a,c){return m(a,c)},{assign:function(d,e){return ib(d,c,e,a.text,a.options)}})}this.tokens.push(d);g&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:g,json:!1}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(g=this.text.substring(this.index+1,this.index+5),g.match(/[\da-f]{4}/i)|| d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?s:b})}function ed(b,a,c,d,e){if(B(d)){b=b(d);if(!b.constant)throw K("ngModel")("constexpr",c,d);return b(a)}return e}function cd(b){function a(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function c(b,c){b=b?"-"+Db(b,"-"):"";a(pf+b,!0===c);a(qf+b,!1===c)}var d=b.ctrl,e=b.$element,f={},g=b.set,h=b.unset,m=b.parentForm,k=b.$animate;d.$setValidity=function(b,e,f){e===s?(d.$pending||
this.throwError("Invalid unicode escape [\\u"+g+"]"),this.index+=4,d+=String.fromCharCode(parseInt(g,16))):d=(f=Qd[g])?d+f:d+g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var Ya=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};Ya.ZERO=function(){return 0};Ya.prototype={constructor:Ya,parse:function(a,c){this.text=a;this.json=c;this.tokens= (d.$pending={}),g(d.$pending,b,f)):(d.$pending&&h(d.$pending,b,f),fd(d.$pending)&&(d.$pending=s));"boolean"!==typeof e?(h(d.$error,b,f),h(d.$$success,b,f)):e?(h(d.$error,b,f),g(d.$$success,b,f)):(g(d.$error,b,f),h(d.$$success,b,f));d.$pending?(a(gd,!0),d.$valid=d.$invalid=s,c("",null)):(a(gd,!1),d.$valid=fd(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?s:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);m.$setValidity(b,e,d)};c("",!0)}function fd(b){if(b)for(var a in b)return!1;
this.lexer.lex(a);c&&(this.assignment=this.logicalOR,this.functionCall=this.fieldAccess=this.objectIndex=this.filterChain=function(){this.throwError("is not valid json",{text:a,index:0})});var d=c?this.primary():this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);d.literal=!!d.literal;d.constant=!!d.constant;return d},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration(); return!0}function Vb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){if(!L(a)){if(C(a))return a.split(" ");if(S(a)){var b=[];r(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function m(a,b){var c=g.data("$classCounts")||{},d=[];r(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});
else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);c.json&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw pa("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw pa("ueoe", g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||f.$index%2===a){var k=e(b||[]);if(!n){var q=m(k,1);h.$addClass(q)}else if(!ra(b,n)){var r=e(n),q=d(k,r),k=d(r,k),q=m(q,1),k=m(k,-1);q&&q.length&&c.addClass(g,q);k&&k.length&&c.removeClass(g,k)}}n=qa(b)}var n;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=m(k,1),h.$addClass(g)):(g=m(k,-1),h.$removeClass(g))}})}}}]}
this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var f=this.tokens[0],g=f.text;if(g===a||g===c||g===d||g===e||!(a||c||d||e))return f}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.json&&!a.json&&this.throwError("is not valid json",a),this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return u(function(d,e){return a(d,e,c)},{constant:c.constant})}, var rf=/^\/(.+)\/([a-z]*)$/,P=function(b){return C(b)?b.toLowerCase():b},Ab=Object.prototype.hasOwnProperty,ib=function(b){return C(b)?b.toUpperCase():b},X,G,la,Ta=[].slice,sf=[].push,Ga=Object.prototype.toString,Sa=K("ng"),Ea=t.angular||(t.angular={}),Va,bb=0;X=U((/msie (\d+)/.exec(P(navigator.userAgent))||[])[1]);isNaN(X)&&(X=U((/trident\/.*; rv:(\d+)/.exec(P(navigator.userAgent))||[])[1]));w.$inject=[];Pa.$inject=[];var L=Array.isArray,ba=function(b){return C(b)?b.trim():b},Ua=function(){if(B(Ua.isActive_))return Ua.isActive_;
ternaryFn:function(a,c,d){return u(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return u(function(e,f){return c(e,f,a,d)},{constant:a.constant&&d.constant})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,f=0;f<a.length;f++){var g=a[f];g&&(e=g(c,d))}return e}},filterChain:function(){for(var a= var b=!(!Y.querySelector("[ng-csp]")&&!Y.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return Ua.isActive_=b},fb=["ng-","data-ng-","ng:","x-ng-"],yd=/[A-Z]/g,hc=!1,Eb,Cd={full:"1.3.0-rc.1",major:1,minor:3,dot:0,codeName:"backyard-atomicity"};V.expando="ng339";var ob=V.cache={},Te=1;V._data=function(b){return this.cache[b[this.expando]]||{}};var Oe=/([\:\-\_]+(.))/g,Pe=/^moz([A-Z])/,tf={mouseleave:"mouseout",mouseenter:"mouseover"},Hb=K("jqLite"),Se=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,
this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=function(a,e,h){h=[h];for(var m=0;m<d.length;m++)h.push(d[m](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+ Gb=/<|&#?\w+;/,Qe=/<([\w:]+)/,Re=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ia={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option;ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead;ia.th=ia.td;var Ia=V.prototype={ready:function(b){function a(){c||(c=
this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,f){return a.assign(d,c(d,f),f)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c= !0,b())}var c=!1;"complete"===Y.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),V(t).on("load",a),this.on("DOMContentLoaded",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?G(this[b]):G(this[this.length+b])},length:0,push:sf,sort:[].sort,splice:[].splice},qb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){qb[P(b)]=b});var zc={};r("input select option textarea button form details".split(" "),
this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a= function(b){zc[b]=!0});var Ac={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};r({data:Jb,removeData:mb},function(b,a){V[a]=b});r({data:Jb,inheritedData:pb,scope:function(b){return G.data(b,"$scope")||pb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return G.data(b,"$isolateScope")||G.data(b,"$isolateScopeNoTemplate")},controller:vc,injector:function(b){return pb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:jb,
this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(Ya.ZERO,a.fn,this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=rc(d,this.options,this.text);return u(function(c,d,h){return e(h||a(c,d),d)},{assign:function(e,g,h){return ib(a(e,h),d,g,c.text,c.options)}})},objectIndex:function(a){var c= css:function(b,a,c){a=Wa(a);if(B(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=P(a);if(qb[d])if(B(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||w).specified?d:s;else if(B(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?s:b},prop:function(b,a,c){if(B(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(F(b)){var d=a.nodeType;return 1===d||3===d?a.textContent:""}a.textContent=
this,d=this.expression();this.consume("]");return u(function(e,f){var g=a(e,f),h=ha(d(e,f),c.text,!0),m;if(!g)return s;(g=Xa(g[h],c.text))&&(g.then&&c.options.unwrapPromises)&&(m=g,"$$v"in g||(m.$$v=s,m.then(function(a){m.$$v=a})),g=g.$$v);return g},{assign:function(e,f,g){var h=ha(d(e,g),c.text);return Xa(a(e,g),c.text)[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var h= b}b.$dv="";return b}(),val:function(b,a){if(F(a)){if(b.multiple&&"select"===pa(b)){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(F(a))return b.innerHTML;lb(b,!0);b.innerHTML=a},empty:wc},function(b,a){V.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==wc&&(2==b.length&&b!==jb&&b!==vc?a:d)===s){if(S(a)){for(e=0;e<g;e++)if(b===Jb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;
[],m=c?c(f,g):f,k=0;k<d.length;k++)h.push(d[k](f,g));k=a(f,g,m)||t;Xa(m,e.text);Xa(k,e.text);h=k.apply?k.apply(m,h):k(h[0],h[1],h[2],h[3],h[4]);return Xa(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return u(function(c,d){for(var g=[],h=0;h<a.length;h++)g.push(a[h](c,d));return g},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{var d= g=e===s?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});r({removeData:mb,on:function a(c,d,e,f){if(B(f))throw Hb("onargs");if(rc(c)){var g=nb(c,!0);f=g.events;var h=g.handle;h||(h=g.handle=Ve(c,f));for(var g=0<=d.indexOf(" ")?d.split(" "):[d],m=g.length;m--;){d=g[m];var k=f[d];k||(f[d]=[],"mouseenter"===d||"mouseleave"===d?a(c,tf[d],function(a){var c=a.relatedTarget;c&&(c===this||this.contains(c))||h(a,d)}):"$destroy"!==d&&c.addEventListener(d,
this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return u(function(c,d){for(var e={},m=0;m<a.length;m++){var k=a[m];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Ib={},ra=L("$sce"),ca={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},R=O.createElement("a"),uc=xa(W.location.href,!0);vc.$inject=["$provide"];wc.$inject=["$locale"];yc.$inject=["$locale"];var Bc= h,!1),k=f[d]);k.push(e)}}},off:uc,one:function(a,c,d){a=G(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;lb(a);r(new V(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(1===d||11===d){c=new V(c);for(var d=0,e=c.length;d<
".",Kd={yyyy:V("FullYear",4),yy:V("FullYear",2,0,!0),y:V("FullYear",1),MMMM:jb("Month"),MMM:jb("Month",!0),MM:V("Month",2,1),M:V("Month",1,1),dd:V("Date",2),d:V("Date",1),HH:V("Hours",2),H:V("Hours",1),hh:V("Hours",2,-12),h:V("Hours",1,-12),mm:V("Minutes",2),m:V("Minutes",1),ss:V("Seconds",2),s:V("Seconds",1),sss:V("Milliseconds",3),EEEE:jb("Day"),EEE:jb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Kb(Math[0< e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;r(new V(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=G(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:xc,detach:function(a){xc(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new V(c);for(var f=0,g=c.length;f<g;f++){var h=c[f];e.insertBefore(h,d.nextSibling);d=h}},addClass:Lb,removeClass:Kb,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=
a?"floor":"ceil"](a/60),2)+Kb(Math.abs(a%60),2))}},Jd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,Id=/^\-?\d+$/;xc.$inject=["$locale"];var Gd=aa(w),Hd=aa(Ga);zc.$inject=["$parse"];var Rd=aa({restrict:"E",compile:function(a,c){8>=P&&(c.href||c.name||c.$set("href",""),a.append(O.createComment("IE fix")));return function(a,c){c.on("click",function(a){c.attr("href")||a.preventDefault()})}}}),Mb={};q(fb,function(a,c){if("multiple"!=a){var d=ka("ng-"+c);Mb[d]=function(){return{priority:100, d;F(f)&&(f=!jb(a,c));(f?Lb:Kb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Ib,triggerHandler:function(a,c,d){var e,f;e=c.type||c;var g=nb(a);if(g=(g=g&&g.events)&&g[e])e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopPropagation:w,type:e,target:a},c.type&&(e=E(e,c)),
compile:function(){return function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}}});q(["src","srcset","href"],function(a){var c=ka("ng-"+a);Mb[c]=function(){return{priority:99,link:function(d,e,f){f.$observe(c,function(c){c&&(f.$set(a,c),P&&e.prop(a,f[a]))})}}}});var mb={$addControl:t,$removeControl:t,$setValidity:t,$setDirty:t,$setPristine:t};Cc.$inject=["$element","$attrs","$scope"];var Ec=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Cc,compile:function(){return{pre:function(a, c=qa(g),f=d?[e].concat(d):[e],r(c,function(c){c.apply(a,f)})}},function(a,c){V.prototype[c]=function(c,e,f){for(var g,h=0,m=this.length;h<m;h++)F(g)?(g=a(this[h],c,e,f),B(g)&&(g=G(g))):tc(g,a(this[h],c,e,f));return B(g)?g:this};V.prototype.bind=V.prototype.on;V.prototype.unbind=V.prototype.off});Xa.prototype={put:function(a,c){this[Ka(a,this.nextUid)]=c},get:function(a){return this[Ka(a,this.nextUid)]},remove:function(a){var c=this[a=Ka(a,this.nextUid)];delete this[a];return c}};var Cc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,
e,f,g){if(!f.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};Dc(e[0],"submit",h);e.on("$destroy",function(){c(function(){Ab(e[0],"submit",h)},0,!1)})}var m=e.parent().controller("form"),k=f.name||f.ngForm;k&&ib(a,k,g,k);if(m)e.on("$destroy",function(){m.$removeControl(g);k&&ib(a,k,s,k);u(g,mb)})}}}}}]},Sd=Ec(),Td=Ec(!0),Ud=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Vd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/,Wd= Xe=/,/,Ye=/^\s*(_?)(\S+?)\1\s*$/,Bc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,La=K("$injector");Cb.$$annotate=Mb;var uf=K("$animate"),oe=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw uf("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$$q","$$asyncCallback",function(a,d){function e(){f||
/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Fc={text:ob,number:function(a,c,d,e,f,g){ob(a,c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Wd.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);if(!e.$isEmpty(a)&&a<c)return e.$setValidity("min",!1),s;e.$setValidity("min",!0);return a},e.$parsers.push(a),e.$formatters.push(a)); (f=a.defer(),d(function(){f.resolve();f=null}));return f.promise}var f;return{enter:function(a,c,d){d?d.after(a):c.prepend(a);return e()},leave:function(a){a.remove();return e()},move:function(a,c,d){return this.enter(a,c,d)},addClass:function(a,c){c=C(c)?c:L(c)?c.join(" "):"";r(a,function(a){Lb(a,c)});return e()},removeClass:function(a,c){c=C(c)?c:L(c)?c.join(" "):"";r(a,function(a){Kb(a,c)});return e()},setClass:function(a,c,d){this.addClass(a,c);this.removeClass(a,d);return e()},enabled:w,cancel:w}}]}],
d.max&&(a=function(a){var c=parseFloat(d.max);if(!e.$isEmpty(a)&&a>c)return e.$setValidity("max",!1),s;e.$setValidity("max",!0);return a},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){if(e.$isEmpty(a)||qb(a))return e.$setValidity("number",!0),a;e.$setValidity("number",!1);return s})},url:function(a,c,d,e,f,g){ob(a,c,d,e,f,g);a=function(a){if(e.$isEmpty(a)||Ud.test(a))return e.$setValidity("url",!0),a;e.$setValidity("url",!1);return s};e.$formatters.push(a);e.$parsers.push(a)}, ja=K("$compile");jc.$inject=["$provide","$$sanitizeUriProvider"];var af=/^(x[\:\-_]|data[\:\-_])/i,Pb=K("$interpolate"),vf=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,df={http:80,https:443,ftp:21},sb=K("$location");Mc.prototype=Sb.prototype=Lc.prototype={$$html5:!1,$$replace:!1,absUrl:tb("$$absUrl"),url:function(a){if(F(a))return this.$$url;a=vf.exec(a);a[1]&&this.path(decodeURIComponent(a[1]));(a[2]||a[1])&&this.search(a[3]||"");this.hash(a[5]||"");return this},protocol:tb("$$protocol"),host:tb("$$host"),
email:function(a,c,d,e,f,g){ob(a,c,d,e,f,g);a=function(a){if(e.$isEmpty(a)||Vd.test(a))return e.$setValidity("email",!0),a;e.$setValidity("email",!1);return s};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){x(d.name)&&c.attr("name",Za());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,g=d.ngFalseValue;D(f)|| port:tb("$$port"),path:Nc("$$path",function(a){a=a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(C(a)||ea(a))a=a.toString(),this.$$search=fc(a);else if(S(a))r(a,function(c,e){null==c&&delete a[e]}),this.$$search=a;else throw sb("isrcharg");break;default:F(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Nc("$$hash",function(a){return a?a.toString():""}),replace:function(){this.$$replace=
(f=!0);D(g)||(g=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:t,button:t,submit:t,reset:t},Gc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,f,g){g&&(Fc[w(f.type)]||Fc.text)(d,e,f,g,c,a)}}}],lb="ng-valid",kb="ng-invalid",Ha="ng-pristine", !0;return this}};var oa=K("$parse"),wf=Function.prototype.call,xf=Function.prototype.apply,yf=Function.prototype.bind,hd=Object.create(null);r({"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:function(){}},function(a,c){a.constant=a.literal=a.sharedGetter=!0;hd[c]=a});var Wb=E(Object.create(null),{"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return B(d)?B(e)?d+e:d:B(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(B(d)?d:0)-(B(e)?e:0)},"*":function(a,
nb="ng-dirty",Xd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,f){function g(a,c){c=c?"-"+cb(c,"-"):"";e.removeClass((a?kb:lb)+c).addClass((a?lb:kb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var h=f(d.ngModel),m=h.assign;if(!m)throw L("ngModel")("nonassign",d.ngModel,ea(e));this.$render=t;this.$isEmpty=function(a){return x(a)|| c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":w,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,
""===a||null===a||a!==a};var k=e.inheritedData("$formController")||mb,l=0,n=this.$error={};e.addClass(Ha);g(!0);this.$setValidity=function(a,c){n[a]!==!c&&(c?(n[a]&&l--,l||(g(!0),this.$valid=!0,this.$invalid=!1)):(g(!1),this.$invalid=!0,this.$valid=!1,l++),n[a]=!c,g(c,a),k.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;e.removeClass(nb).addClass(Ha)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,e.removeClass(Ha).addClass(nb), c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}}),zf={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Tb=function(a){this.options=a};Tb.prototype={constructor:Tb,lex:function(a){this.text=a;this.index=0;this.ch=s;for(this.tokens=[];this.index<this.text.length;)if(this.ch=this.text.charAt(this.index),
k.$setDirty());q(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,m(a,d),q(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var r=this;a.$watch(function(){var c=h(a);if(r.$modelValue!==c){var d=r.$formatters,e=d.length;for(r.$modelValue=c;e--;)c=d[e](c);r.$viewValue!==c&&(r.$viewValue=c,r.$render())}})}],Yd=function(){return{require:["ngModel","^?form"],controller:Xd,link:function(a,c,d,e){var f=e[0],g=e[1]||mb;g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})}}}, this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent();else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch}),this.index++;else if(this.isWhitespace(this.ch))this.index++;else{a=this.ch+this.peek();var c=a+this.peek(2),d=Wb[this.ch],e=Wb[a],f=Wb[c];f?(this.tokens.push({index:this.index,text:c,fn:f}),this.index+=3):e?(this.tokens.push({index:this.index,
Zd=aa({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),Hc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},$d=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&& text:a,fn:e}),this.index+=2):d?(this.tokens.push({index:this.index,text:this.ch,fn:d}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=
RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!x(a)){var c=[];a&&q(a.split(f),function(a){a&&c.push(Y(a))});return c}});e.$formatters.push(function(a){return J(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},ae=/^(true|false|\d+)$/,be=function(){return{priority:100,compile:function(a,c){return ae.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},ce=sa(function(a,c,d){c.addClass("ng-binding").data("$binding", a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=B(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw oa("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=P(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&
d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),de=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],ee=["$sce","$parse",function(a,c){return function(d,e,f){e.addClass("ng-binding").data("$binding",f.ngBindHtml);var g=c(f.ngBindHtml);d.$watch(function(){return(g(d)||"").toString()},function(c){e.html(a.getTrustedHtml(g(d))||"")})}}],fe=Lb("",!0),ge= e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,constant:!0,fn:function(){return a}})},readIdent:function(){for(var a=this.text,c="",d=this.index,e,f,g,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;this.index++}e&&"."===
Lb("Odd",0),he=Lb("Even",1),ie=sa({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),je=[function(){return{scope:!0,controller:"@"}}],Ic={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=ka("ng-"+a);Ic[c]=["$parse",function(d){return{compile:function(e,f){var g=d(f[c]);return function(c,d,e){d.on(w(a),function(a){c.$apply(function(){g(c,{$event:a})})})}}}}]}); c[c.length-1]&&(this.index--,c=c.slice(0,-1),e=c.lastIndexOf("."),-1===e&&(e=s));if(e)for(f=this.index;f<this.text.length;){h=this.text.charAt(f);if("("===h){g=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(h))f++;else break}this.tokens.push({index:d,text:c,fn:hd[c]||Pc(c,this.options,a)});g&&(this.tokens.push({index:e,text:"."}),this.tokens.push({index:e+1,text:g}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=
var ke=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,compile:function(c,d,e){return function(c,d,h){var m,k;c.$watch(h.ngIf,function(l){Na(l)?(k=c.$new(),e(k,function(c){m={startNode:c[0],endNode:c[c.length++]=O.createComment(" end ngIf: "+h.ngIf+" ")};a.enter(c,d.parent(),d)})):(k&&(k.$destroy(),k=null),m&&(a.leave(vb(m)),m=null))})}}}}],le=["$http","$templateCache","$anchorScroll","$compile","$animate","$sce",function(a,c,d,e,f,g){return{restrict:"ECA", this.text.charAt(this.index),e=e+g;if(f)"u"===g?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d+=zf[g]||g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,string:d,constant:!0,fn:function(){return d}});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var Za=function(a,c,d){this.lexer=a;this.$filter=c;this.options=
priority:400,terminal:!0,transclude:"element",compile:function(h,m,k){var l=m.ngInclude||m.src,n=m.onload||"",r=m.autoscroll;return function(h,m){var q=0,s,t,y=function(){s&&(s.$destroy(),s=null);t&&(f.leave(t),t=null)};h.$watch(g.parseAsResourceUrl(l),function(g){var l=function(){!z(r)||r&&!h.$eval(r)||d()},x=++q;g?(a.get(g,{cache:c}).success(function(a){if(x===q){var c=h.$new();k(c,function(d){y();s=c;t=d;t.html(a);f.enter(t,null,m,l);e(t.contents())(s);s.$emit("$includeContentLoaded");h.$eval(n)})}}).error(function(){x=== d};Za.ZERO=E(function(){return 0},{sharedGetter:!0,constant:!0});Za.prototype={constructor:Za,parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();
q&&y()}),h.$emit("$includeContentRequested")):y()})}}}}],me=sa({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),ne=sa({terminal:!0,priority:1E3}),oe=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var h=g.count,m=g.$attr.when&&f.attr(g.$attr.when),k=g.offset||0,l=e.$eval(m)||{},n={},r=c.startSymbol(),p=c.endSymbol(),s=/^when(Minus)?(.+)$/;q(g,function(a,c){s.test(c)&&(l[w(c.replace("when","").replace("Minus","-"))]=f.attr(g.$attr[c]))}); (a=c.fn)||this.throwError("not a primary expression",c);c.constant&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw oa("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw oa("ueoe",this.text);return this.tokens[0]},
q(l,function(a,e){n[e]=c(a.replace(d,r+h+"-"+k+p))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-k));return n[c](e,f,!0)},function(a){f.text(a)})}}}],pe=["$parse","$animate",function(a,c){var d=L("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(e,f,g){return function(e,f,k){var l=k.ngRepeat,n=l.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),r,p,s,t,z,A,x,G={$id:Ca};if(!n)throw d("iexp",l);k= peek:function(a,c,d,e){if(0<this.tokens.length){var f=this.tokens[0],g=f.text;if(g===a||g===c||g===d||g===e||!(a||c||d||e))return f}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return E(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return E(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&
n[1];z=n[2];(n=n[4])?(r=a(n),p=function(a,c,d){x&&(G[x]=a);G[A]=c;G.$index=d;return r(e,G)}):(s=function(a,c){return Ca(c)},t=function(a){return a});n=k.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!n)throw d("iidexp",k);A=n[3]||n[1];x=n[2];var v={};e.$watchCollection(z,function(a){var k,n,r=f[0],z,M={},G,N,w,I,D,u,J=[];if(pb(a))D=a,z=p||s;else{z=p||t;D=[];for(w in a)a.hasOwnProperty(w)&&"$"!=w.charAt(0)&&D.push(w);D.sort()}G=D.length;n=J.length=D.length;for(k=0;k<n;k++)if(w=a===D?k: c.constant&&d.constant})},binaryFn:function(a,c,d){return E(function(e,f){return c(e,f,a,d)},{constant:a.constant&&d.constant})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,f=0,g=a.length;f<g;f++)e=a[f](c,d);return e}},filterChain:function(){for(var a=this.expression(),c;c=this.expect("|");)a=this.binaryFn(a,c.fn,this.filter());return a},filter:function(){var a=
D[k],I=a[w],I=z(w,I,k),na(I,"`track by` id"),v.hasOwnProperty(I))u=v[I],delete v[I],M[I]=u,J[k]=u;else{if(M.hasOwnProperty(I))throw q(J,function(a){a&&a.startNode&&(v[a.id]=a)}),d("dupes",l,I);J[k]={id:I};M[I]=!1}for(w in v)v.hasOwnProperty(w)&&(u=v[w],k=vb(u),c.leave(k),q(k,function(a){a.$$NG_REMOVED=!0}),u.scope.$destroy());k=0;for(n=D.length;k<n;k++){w=a===D?k:D[k];I=a[w];u=J[k];J[k-1]&&(r=J[k-1].endNode);if(u.startNode){N=u.scope;z=r;do z=z.nextSibling;while(z&&z.$$NG_REMOVED);u.startNode!=z&& this.expect(),c=this.$filter(a.text),d,e;if(this.peek(":"))for(d=[],e=[];this.expect(":");)d.push(this.expression());return da(function(a,g,h){if(e){e[0]=h;for(h=d.length;h--;)e[h+1]=d[h](a,g);return c.apply(s,e)}return c(h)})},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,f){return a.assign(d,
c.move(vb(u),null,y(r));r=u.endNode}else N=e.$new();N[A]=I;x&&(N[x]=w);N.$index=k;N.$first=0===k;N.$last=k===G-1;N.$middle=!(N.$first||N.$last);N.$odd=!(N.$even=0===(k&1));u.startNode||g(N,function(a){a[a.length++]=O.createComment(" end ngRepeat: "+l+" ");c.enter(a,null,y(r));r=a;u.scope=N;u.startNode=r&&r.endNode?r.endNode:a[0];u.endNode=a[a.length-1];M[u.id]=u})}v=M})}}}}],qe=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Na(c)?"removeClass":"addClass"](d,"ng-hide")})}}], c(d,f),f)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.assignment();if(d=this.expect(":"))return this.ternaryFn(a,c,this.assignment());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;c=this.expect("||");)a=this.binaryFn(a,c.fn,this.logicalAND());return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),
re=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Na(c)?"addClass":"removeClass"](d,"ng-hide")})}}],se=sa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),te=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g,h,m=[];c.$watch(e.ngSwitch||e.on,function(d){for(var l=0,n=m.length;l<n;l++)m[l].$destroy(),a.leave(h[l]);h=[]; c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;
m=[];if(g=f.cases["!"+d]||f.cases["?"])c.$eval(e.change),q(g,function(d){var e=c.$new();m.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],ue=sa({transclude:"element",priority:800,require:"^ngSwitch",compile:function(a,c,d){return function(a,f,g,h){h.cases["!"+c.ngSwitchWhen]=h.cases["!"+c.ngSwitchWhen]||[];h.cases["!"+c.ngSwitchWhen].push({transclude:d,element:f})}}}),ve=sa({transclude:"element",priority:800,require:"^ngSwitch",compile:function(a,c,d){return function(a, return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(Za.ZERO,a.fn,this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this.text,d=this.expect().text,e=Pc(d,this.options,c);return E(function(c,d,h){return e(h||a(c,d))},{assign:function(e,g,h){(h=a(e,h))||a.assign(e,h={});return ub(h,d,g,c)}})},objectIndex:function(a){var c=this.text,d=this.expression();this.consume("]");return E(function(e,f){var g=a(e,f),h=d(e,f);na(h,
c,g,h){h.cases["?"]=h.cases["?"]||[];h.cases["?"].push({transclude:d,element:c})}}}),we=sa({controller:["$element","$transclude",function(a,c){if(!c)throw L("ngTransclude")("orphan",ea(a));this.$transclude=c}],link:function(a,c,d,e){e.$transclude(function(a){c.html("");c.append(a)})}}),xe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],ye=L("ngOptions"),ze=aa({terminal:!0}),Ae=["$compile","$parse",function(a, c);return g?Ba(g[h],c):s},{assign:function(e,f,g){var h=na(d(e,g),c);(g=Ba(a(e,g),c))||a.assign(e,g={});return g[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this.text,f=d.length?[]:null;return function(g,h){var m=c?c(g,h):g,k=a(g,h,m)||w;if(f)for(var n=d.length;n--;)f[n]=Ba(d[n](g,h),e);Ba(m,e);if(k){if(k.constructor===k)throw oa("isecfn",e);if(k===wf||k===xf||k===yf)throw oa("isecff",e);
c){var d=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,e={$setViewValue:t};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},l=e,n;m.databound=d.ngModel;m.init=function(a,c,d){l=a;n=d};m.addOption=function(c){na(c,'"option value"');k[c]=!0;l.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};m.removeOption= }m=k.apply?k.apply(m,f):k(f[0],f[1],f[2],f[3],f[4]);return Ba(m,e)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return E(function(c,d){for(var g=[],h=0,m=a.length;h<m;h++)g.push(a[h](c,d));return g},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.expect(),d=d.string||
function(a){this.hasOption(a)&&(delete k[a],l.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Ca(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",!0)};m.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){m.renderUnknownOption=t})}],link:function(e,g,h,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(v.parent()&&v.remove(),c.val(a),""===a&&A.prop("selected",!0)):x(a)&&A?c.val(""):e.renderUnknownOption(a)}; d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return E(function(c,d){for(var e={},m=0,k=a.length;m<k;m++){var n=a[m];e[n.key]=n.value(c,d)}return e},{literal:!0,constant:c})}};var Qc=Object.create(null),Ca=K("$sce"),ka={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ja=K("$compile"),Z=Y.createElement("a"),Tc=Aa(t.location.href,!0);qc.$inject=["$provide"];Uc.$inject=["$locale"];Wc.$inject=["$locale"];
c.on("change",function(){a.$apply(function(){v.parent()&&v.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new Sa(d.$viewValue);q(c.find("option"),function(c){c.selected=z(a.get(c.value))})};a.$watch(function(){Aa(e,d.$viewValue)||(e=da(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k, var Zc=".",nf={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:wb("Month"),MMM:wb("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",1),sss:$("Milliseconds",3),EEEE:wb("Day"),EEE:wb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(vb(Math[0<
s,u,x;u=g.$modelValue;x=r(e)||[];var A=n?Nb(x):x,H,K,B;K={};s=!1;var E,L;if(t)if(v&&J(u))for(s=new Sa([]),B=0;B<u.length;B++)K[m]=u[B],s.put(v(e,K),u[B]);else s=new Sa(u);for(B=0;H=A.length,B<H;B++){k=B;if(n){k=A[B];if("$"===k.charAt(0))continue;K[n]=k}K[m]=x[k];d=p(e,K)||"";(k=a[d])||(k=a[d]=[],c.push(d));t?d=z(s.remove(v?v(e,K):q(e,K))):(v?(d={},d[m]=u,d=v(e,d)===v(e,K)):d=u===q(e,K),s=s||d);E=l(e,K);E=z(E)?E:"";k.push({id:v?v(e,K):n?A[B]:B,label:E,selected:d})}t||(w||null===u?a[""].unshift({id:"", a?"floor":"ceil"](a/60),2)+vb(Math.abs(a%60),2))},ww:ad(2),w:ad(1)},mf=/((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,lf=/^\-?\d+$/;Vc.$inject=["$locale"];var jf=da(P),kf=da(ib);Xc.$inject=["$parse"];var Fd=da({restrict:"E",compile:function(a,c){8>=X&&(c.href||c.name||c.$set("href",""),a.append(Y.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var f="[object SVGAnimatedString]"===Ga.call(c.prop("href"))?"xlink:href":"href";c.on("click",
label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));K=0;for(A=c.length;K<A;K++){d=c[K];k=a[d];y.length<=K?(u={element:G.clone().attr("label",d),label:k.label},x=[u],y.push(x),f.append(u.element)):(x=y[K],u=x[0],u.label!=d&&u.element.attr("label",u.label=d));E=null;B=0;for(H=k.length;B<H;B++)s=k[B],(d=x[B+1])?(E=d.element,d.label!==s.label&&E.text(d.label=s.label),d.id!==s.id&&E.val(d.id=s.id),E[0].selected!==s.selected&&E.prop("selected",d.selected=s.selected)):(""===s.id&&w?L= function(a){c.attr(f)||a.preventDefault()})}}}),kb={};r(qb,function(a,c){if("multiple"!=a){var d=va("ng-"+c);kb[d]=function(){return{restrict:"A",priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}});r(Ac,function(a,c){kb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(rf))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});r(["src","srcset","href"],function(a){var c=
w:(L=D.clone()).val(s.id).attr("selected",s.selected).text(s.label),x.push({element:L,label:s.label,id:s.id,selected:s.selected}),E?E.after(L):u.element.append(L),E=L);for(B++;x.length>B;)x.pop().element.remove()}for(;y.length>K;)y.pop()[0].element.remove()}var k;if(!(k=u.match(d)))throw ye("iexp",u,ea(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],p=c(k[3]||""),q=c(k[2]?k[1]:m),r=c(k[7]),v=k[8]?c(k[8]):null,y=[[{element:f,label:""}]];w&&(a(w)(e),w.removeClass("ng-scope"),w.remove());f.html("");f.on("change", va("ng-"+a);kb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===Ga.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",g=null);f.$observe(c,function(c){c?(f.$set(h,c),X&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var xb={$addControl:w,$removeControl:w,$setValidity:w,$$setPending:w,$setDirty:w,$setPristine:w,$setSubmitted:w,$$clearControlValidity:w};bd.$inject=["$element","$attrs","$scope","$animate"];var id=function(a){return["$timeout",
function(){e.$apply(function(){var a,c=r(e)||[],d={},h,k,l,p,u,x,w;if(t)for(k=[],p=0,x=y.length;p<x;p++)for(a=y[p],l=1,u=a.length;l<u;l++){if((h=a[l].element)[0].selected){h=h.val();n&&(d[n]=h);if(v)for(w=0;w<c.length&&(d[m]=c[w],v(e,d)!=h);w++);else d[m]=c[h];k.push(q(e,d))}}else if(h=f.val(),"?"==h)k=s;else if(""===h)k=null;else if(v)for(w=0;w<c.length;w++){if(d[m]=c[w],v(e,d)==h){k=q(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=q(e,d);g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(m[1]){var r=m[0], function(c){return{name:"form",restrict:a?"EAC":"E",controller:bd,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var h=function(c){a.$apply(function(){g.$commitViewValue();g.$setSubmitted()});c.preventDefault?c.preventDefault():c.returnValue=!1};e[0].addEventListener("submit",h,!1);e.on("$destroy",function(){c(function(){e[0].removeEventListener("submit",h,!1)},0,!1)})}var m=e.parent().controller("form"),k=f.name||f.ngForm;k&&ub(a,k,g,k);if(m)e.on("$destroy",function(){m.$removeControl(g);
p=m[1],t=h.multiple,u=h.ngOptions,w=!1,A,D=y(O.createElement("option")),G=y(O.createElement("optgroup")),v=D.clone();m=0;for(var B=g.children(),E=B.length;m<E;m++)if(""===B[m].value){A=w=B.eq(m);break}r.init(p,w,v);if(t&&(h.required||h.ngRequired)){var L=function(a){p.$setValidity("required",!h.required||a&&a.length);return a};p.$parsers.push(L);p.$formatters.unshift(L);h.$observe("required",function(){L(p.$viewValue)})}u?n(e,g,p):t?l(e,g,p):k(e,g,p,r)}}}}],Be=["$interpolate",function(a){var c={addOption:t, k&&ub(a,k,s,k);E(g,xb)})}}}}}]},Gd=id(),Td=id(!0),of=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,Af=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Bf=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,Cf=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,jd=/^(\d{4})-(\d{2})-(\d{2})$/,kd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d))?$/,Xb=/^(\d{4})-W(\d\d)$/,ld=/^(\d{4})-(\d\d)$/,md=/^(\d\d):(\d\d)(?::(\d\d))?$/,
removeOption:t};return{restrict:"E",priority:100,compile:function(d,e){if(x(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),l=k.data("$selectController")||k.parent().data("$selectController");l&&l.databound?d.prop("selected",!1):l=c;f?a.$watch(f,function(a,c){e.$set("value",a);a!==c&&l.removeOption(c);l.addOption(a)}):l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],Ce=aa({restrict:"E",terminal:!0});(Ba=W.jQuery)?(y= Df=/(\s+|^)default(\s+|$)/,Yb=new K("ngModel"),nd={text:function(a,c,d,e,f,g){$a(a,c,d,e,f,g);Ub(e)},date:ab("date",jd,zb(jd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":ab("datetimelocal",kd,zb(kd,"yyyy MM dd HH mm ss".split(" ")),"yyyy-MM-ddTHH:mm:ss"),time:ab("time",md,zb(md,["HH","mm","ss"]),"HH:mm:ss"),week:ab("week",Xb,function(a){if(fa(a))return a;if(C(a)){Xb.lastIndex=0;var c=Xb.exec(a);if(c){a=+c[1];var d=+c[2],c=$c(a),d=7*(d-1);return new Date(a,0,c.getDate()+d)}}return NaN},"yyyy-Www"),
Ba,u(Ba.fn,{scope:Da.scope,isolateScope:Da.isolateScope,controller:Da.controller,injector:Da.injector,inheritedData:Da.inheritedData}),wb("remove",!0,!0,!1),wb("empty",!1,!1,!1),wb("html",!1,!1,!0)):y=Q;bb.element=y;(function(a){u(a,{bootstrap:Wb,copy:da,extend:u,equals:Aa,element:y,forEach:q,injector:Xb,noop:t,bind:rb,toJson:ma,fromJson:Sb,identity:za,isUndefined:x,isDefined:z,isString:D,isFunction:B,isObject:T,isNumber:qb,isElement:Kc,isArray:J,version:Md,isDate:Ja,lowercase:w,uppercase:Ga,callbacks:{counter:0}, month:ab("month",ld,zb(ld,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){dd(a,c,d,e);$a(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:Cf.test(a)?parseFloat(a):s});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!ea(a))throw Yb("numfmt",a);a=a.toString()}return a});if(d.min||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)||F(h)||a>=h};d.$observe("min",function(a){B(a)&&!ea(a)&&(a=parseFloat(a,10));h=ea(a)&&!isNaN(a)?a:s;e.$validate()})}if(d.max||
$$minErr:L,$$csp:Rb});Ua=Qc(W);try{Ua("ngLocale")}catch(c){Ua("ngLocale",[]).provider("$locale",pd)}Ua("ng",["ngLocale"],["$provide",function(a){a.provider("$compile",ec).directive({a:Rd,input:Gc,textarea:Gc,form:Sd,script:xe,select:Ae,style:Ce,option:Be,ngBind:ce,ngBindHtml:ee,ngBindTemplate:de,ngClass:fe,ngClassEven:he,ngClassOdd:ge,ngCloak:ie,ngController:je,ngForm:Td,ngHide:re,ngIf:ke,ngInclude:le,ngInit:me,ngNonBindable:ne,ngPluralize:oe,ngRepeat:pe,ngShow:qe,ngStyle:se,ngSwitch:te,ngSwitchWhen:ue, d.ngMax){var m;e.$validators.max=function(a){return e.$isEmpty(a)||F(m)||a<=m};d.$observe("max",function(a){B(a)&&!ea(a)&&(a=parseFloat(a,10));m=ea(a)&&!isNaN(a)?a:s;e.$validate()})}},url:function(a,c,d,e,f,g){$a(a,c,d,e,f,g);Ub(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||Af.test(d)}},email:function(a,c,d,e,f,g){$a(a,c,d,e,f,g);Ub(e);e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||Bf.test(d)}},radio:function(a,
ngSwitchDefault:ve,ngOptions:ze,ngTransclude:we,ngModel:Yd,ngList:$d,ngChange:Zd,required:Hc,ngRequired:Hc,ngValue:be}).directive(Mb).directive(Ic);a.provider({$anchorScroll:Zc,$animate:Od,$browser:bd,$cacheFactory:cd,$controller:gd,$document:hd,$exceptionHandler:id,$filter:vc,$interpolate:nd,$interval:od,$http:jd,$httpBackend:kd,$location:rd,$log:sd,$parse:td,$rootScope:wd,$q:ud,$sce:zd,$sceDelegate:yd,$sniffer:Ad,$templateCache:dd,$timeout:Bd,$window:Cd})}])})(bb);y(O).ready(function(){Oc(O,Wb)})})(window, c,d,e){F(d.name)&&c.attr("name",++bb);c.on("click",function(a){c[0].checked&&e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,g,h,m){var k=ed(m,a,"ngTrueValue",d.ngTrueValue,!0),n=ed(m,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&&a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==k};e.$formatters.push(function(a){return ra(a,
document);!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-start{clip:rect(0,auto,auto,0);-ms-zoom:1.0001;}.ng-animate-active{clip:rect(-1px,auto,auto,0);-ms-zoom:1;}</style>'); k)});e.$parsers.push(function(a){return a?k:n})},hidden:w,button:w,submit:w,reset:w,file:w},kc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:function(f,g,h,m){m[0]&&(nd[P(h.type)]||nd.text)(f,g,h,m[0],c,a,d,e)}}}],pf="ng-valid",qf="ng-invalid",Ma="ng-pristine",yb="ng-dirty",gd="ng-pending",Ef=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q",function(a,c,d,e,f,g,h,m,k){this.$modelValue=this.$viewValue=
Number.NaN;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=s;this.$name=d.name;var n=f(d.ngModel),p=null,l=this,q=function(){var c=n(a);l.$options&&l.$options.getterSetter&&D(c)&&(c=c());return c},A=function(c){var d;l.$options&&l.$options.getterSetter&&D(d=n(a))?d(l.$modelValue):n.assign(a,
l.$modelValue)};this.$$setOptions=function(a){l.$options=a;if(!(n.assign||a&&a.getterSetter))throw Yb("nonassign",d.ngModel,ta(e));};this.$render=w;this.$isEmpty=function(a){return F(a)||""===a||null===a||a!==a};var u=e.inheritedData("$formController")||xb,x=0;e.addClass(Ma).addClass("ng-untouched");cd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:u,$animate:g});this.$setPristine=function(){l.$dirty=!1;l.$pristine=!0;g.removeClass(e,yb);g.addClass(e,
Ma)};this.$setUntouched=function(){l.$touched=!1;l.$untouched=!0;g.setClass(e,"ng-untouched","ng-touched")};this.$setTouched=function(){l.$touched=!0;l.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(p);l.$viewValue=l.$$lastCommittedViewValue;l.$render()};this.$validate=function(){ea(l.$modelValue)&&isNaN(l.$modelValue)||this.$$parseAndValidate()};this.$$runValidators=function(a,c,d,e){function f(){var a=!0;r(l.$validators,function(e,f){var h=e(c,
d);a=a&&h;g(f,h)});return a?!0:(r(l.$asyncValidators,function(a,c){g(c,null)}),m(),!1)}function h(){var a=[];r(l.$asyncValidators,function(e,f){var h=e(c,d);if(!h||!D(h.then))throw Yb("$asyncValidators",h);g(f,s);a.push(h.then(function(){g(f,!0)},function(a){g(f,!1)}))});a.length?k.all(a).then(m):m()}function g(a,c){n===x&&l.$setValidity(a,c)}function m(){n===x&&e()}x++;var n=x;(function(a){var c=l.$$parserName||"parse";if(a===s)g(c,null);else if(g(c,a),!a)return r(l.$validators,function(a,c){g(c,
null)}),r(l.$asyncValidators,function(a,c){g(c,null)}),m(),!1;return!0})(a)&&f()&&h()};this.$commitViewValue=function(){var a=l.$viewValue;h.cancel(p);if(l.$$lastCommittedViewValue!==a||""===a&&l.$$hasNativeValidators)l.$$lastCommittedViewValue=a,l.$pristine&&(l.$dirty=!0,l.$pristine=!1,g.removeClass(e,Ma),g.addClass(e,yb),u.$setDirty()),this.$$parseAndValidate()};this.$$parseAndValidate=function(){for(var a=!0,c=l.$$lastCommittedViewValue,d=c,e=0;e<l.$parsers.length;e++)if(d=l.$parsers[e](d),F(d)){a=
!1;break}ea(l.$modelValue)&&isNaN(l.$modelValue)&&(l.$modelValue=q());var f=l.$modelValue,h=l.$options&&l.$options.allowInvalid;h&&(l.$modelValue=d,l.$modelValue!==f&&l.$$writeModelToScope());l.$$runValidators(a,d,c,function(){h||(l.$modelValue=l.$valid?d:s,l.$modelValue!==f&&l.$$writeModelToScope())})};this.$$writeModelToScope=function(){A(l.$modelValue);r(l.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};this.$setViewValue=function(a,c){l.$viewValue=a;l.$options&&!l.$options.updateOnDefault||
l.$$debounceViewValueCommit(c)};this.$$debounceViewValueCommit=function(c){var d=0,e=l.$options;e&&B(e.debounce)&&(e=e.debounce,ea(e)?d=e:ea(e[c])?d=e[c]:ea(e["default"])&&(d=e["default"]));h.cancel(p);d?p=h(function(){l.$commitViewValue()},d):m.$$phase?l.$commitViewValue():a.$apply(function(){l.$commitViewValue()})};a.$watch(function(){var a=q();if(a!==l.$modelValue){l.$modelValue=a;for(var c=l.$formatters,d=c.length,e=a;d--;)e=c[d](e);l.$viewValue!==e&&(l.$viewValue=l.$$lastCommittedViewValue=e,
l.$render(),l.$$runValidators(s,a,e,w))}return a})}],he=function(){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:Ef,link:{pre:function(a,c,d,e){var f=e[0],g=e[1]||xb;f.$$setOptions(e[2]&&e[2].$options);g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})},post:function(a,c,d,e){var f=e[0];if(f.$options&&f.$options.updateOn)c.on(f.$options.updateOn,function(a){f.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(c){f.$touched||a.$apply(function(){f.$setTouched()})})}}}},
je=da({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),mc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){e&&(d.required=!0,e.$validators.required=function(a,c){return!d.required||!e.$isEmpty(c)},d.$observe("required",function(){e.$validate()}))}}},lc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f,g=d.ngPattern||d.pattern;d.$observe("pattern",function(a){C(a)&&0<a.length&&
(a=new RegExp(a));if(a&&!a.test)throw K("ngPattern")("noregexp",g,a,ta(c));f=a||s;e.$validate()});e.$validators.pattern=function(a){return e.$isEmpty(a)||F(f)||f.test(a)}}}}},oc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("maxlength",function(a){f=U(a)||0;e.$validate()});e.$validators.maxlength=function(a,c){return e.$isEmpty(c)||c.length<=f}}}}},nc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",
function(a){f=U(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}},ie=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",g="false"!==d.ngTrim,h=g?ba(f):f;e.$parsers.push(function(a){if(!F(a)){var c=[];a&&r(a.split(h),function(a){a&&c.push(g?ba(a):a)});return c}});e.$formatters.push(function(a){return L(a)?a.join(f):s});e.$isEmpty=function(a){return!a||!a.length}}}},Ff=/^(true|false|\d+)$/,
ke=function(){return{restrict:"A",priority:100,compile:function(a,c){return Ff.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},le=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=a.$eval(c.ngModelOptions);this.$options.updateOn!==s?(this.$options.updateOnDefault=!1,this.$options.updateOn=ba(this.$options.updateOn.replace(Df,function(){d.$options.updateOnDefault=
!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ld=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);c.$watch(f.ngBind,function(a){e.text(a==s?"":a)})}}}}],Nd=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate));c.$$addBindingInfo(f,d.expressions);g.$observe("ngBindTemplate",function(a){f.text(a)})}}}}],Md=
["$sce","$parse","$compile",function(a,c,d){return{restrict:"A",compile:function(e,f){var g=c(f.ngBindHtml),h=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(h,function(){e.html(a.getTrustedHtml(g(c))||"")})}}}}],Od=Vb("",!0),Qd=Vb("Odd",0),Pd=Vb("Even",1),Rd=Fa({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),Sd=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],
pc={},Gf={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=va("ng-"+a);pc[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,g){var h=d(g[c]);return function(c,d){var f=P(a);d.on(f,function(a){var d=function(){h(c,{$event:a})};Gf[f]&&e.$$phase?c.$evalAsync(d):c.$apply(d)})}}}}]});var Vd=["$animate",function(a){return{multiElement:!0,
transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,m,k;c.$watch(e.ngIf,function(c){c?m||g(function(c,f){m=f;c[c.length++]=Y.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),m&&(m.$destroy(),m=null),h&&(k=hb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],Wd=["$templateRequest","$anchorScroll","$animate","$sce",function(a,c,d,e){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",
controller:Ea.noop,compile:function(f,g){var h=g.ngInclude||g.src,m=g.onload||"",k=g.autoscroll;return function(f,g,l,q,r){var u=0,s,z,t,v=function(){z&&(z.remove(),z=null);s&&(s.$destroy(),s=null);t&&(d.leave(t).then(function(){z=null}),z=t,t=null)};f.$watch(e.parseAsResourceUrl(h),function(e){var h=function(){!B(k)||k&&!f.$eval(k)||c()},l=++u;e?(a(e,!0).then(function(a){if(l===u){var c=f.$new();q.template=a;a=r(c,function(a){v();d.enter(a,null,g).then(h)});s=c;t=a;s.$emit("$includeContentLoaded");
f.$eval(m)}},function(){l===u&&(v(),f.$emit("$includeContentError"))}),f.$emit("$includeContentRequested")):(v(),q.template=null)})}}}}],me=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(sc(f.template,Y).childNodes)(c,function(a){d.append(a)},s,s,d)):(d.html(f.template),a(d.contents())(c))}}}],Xd=Fa({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Yd=Fa({terminal:!0,priority:1E3}),
Zd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var h=g.count,m=g.$attr.when&&f.attr(g.$attr.when),k=g.offset||0,n=e.$eval(m)||{},p={},l=c.startSymbol(),q=c.endSymbol(),s=/^when(Minus)?(.+)$/;r(g,function(a,c){s.test(c)&&(n[P(c.replace("when","").replace("Minus","-"))]=f.attr(g.$attr[c]))});r(n,function(a,e){p[e]=c(a.replace(d,l+h+"-"+k+q))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in n||(c=a.pluralCat(c-k));return p[c](e)},
function(a){f.text(a)})}}}],$d=["$parse","$animate",function(a,c){var d=K("ngRepeat"),e=function(a,c,d,e,k,n,p){a[d]=e;k&&(a[k]=n);a.$index=c;a.$first=0===c;a.$last=c===p-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(c&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,m=Y.createComment(" end ngRepeat: "+h+" "),k=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
if(!k)throw d("iexp",h);var n=k[1],p=k[2],l=k[3],q=k[4],k=n.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!k)throw d("iidexp",n);var A=k[3]||k[1],u=k[2];if(l&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(l)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent)$/.test(l)))throw d("badident",l);var t,z,B,v,w={$id:Ka};q?t=a(q):(B=function(a,c){return Ka(c)},v=function(a){return a});return function(a,f,g,k,n){t&&(z=function(c,d,e){u&&(w[u]=c);w[A]=d;w.$index=e;return t(a,w)});
var q=Object.create(null);a.$watchCollection(p,function(g){var k,p,t=f[0],J,x=Object.create(null),w,N,E,F,y,C,ga;l&&(a[l]=g);if(Na(g))y=g,p=z||B;else{p=z||v;y=[];for(ga in g)g.hasOwnProperty(ga)&&"$"!=ga.charAt(0)&&y.push(ga);y.sort()}w=y.length;ga=Array(w);for(k=0;k<w;k++)if(N=g===y?k:y[k],E=g[N],F=p(N,E,k),q[F])C=q[F],delete q[F],x[F]=C,ga[k]=C;else{if(x[F])throw r(ga,function(a){a&&a.scope&&(q[a.id]=a)}),d("dupes",h,F,sa(E));ga[k]={id:F,scope:s,clone:s};x[F]=!0}for(J in q){C=q[J];F=hb(C.clone);
c.leave(F);if(F[0].parentNode)for(k=0,p=F.length;k<p;k++)F[k].$$NG_REMOVED=!0;C.scope.$destroy()}for(k=0;k<w;k++)if(N=g===y?k:y[k],E=g[N],C=ga[k],C.scope){J=t;do J=J.nextSibling;while(J&&J.$$NG_REMOVED);C.clone[0]!=J&&c.move(hb(C.clone),null,G(t));t=C.clone[C.clone.length-1];e(C.scope,k,A,E,u,N,w)}else n(function(a,d){C.scope=d;var f=m.cloneNode(!1);a[a.length++]=f;c.enter(a,null,G(t));t=f;C.clone=a;x[C.id]=C;e(C.scope,k,A,E,u,N,w)});q=x})}}}}],ae=["$animate",function(a){return{restrict:"A",multiElement:!0,
link:function(c,d,e){c.$watch(e.ngShow,function(c){a[c?"removeClass":"addClass"](d,"ng-hide")})}}}],Ud=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngHide,function(c){a[c?"addClass":"removeClass"](d,"ng-hide")})}}}],be=Fa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),ce=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,
d,e,f){var g=[],h=[],m=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=m.length;d<e;++d)a.cancel(m[d]);d=m.length=0;for(e=k.length;d<e;++d){var s=hb(h[d].clone);k[d].$destroy();(m[d]=a.leave(s)).then(n(m,d))}h.length=0;k.length=0;(g=f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=Y.createComment(" end ngSwitchWhen: ");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],
de=Fa({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),ee=Fa({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),ge=Fa({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw K("ngTransclude")("orphan",ta(c));f(function(a){c.empty();
c.append(a)})}}),Hd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],Hf=K("ngOptions"),fe=da({restrict:"A",terminal:!0}),Id=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,e={$setViewValue:w};return{restrict:"E",require:["select",
"?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},n=e,p;m.databound=d.ngModel;m.init=function(a,c,d){n=a;p=d};m.addOption=function(c,d){Ja(c,'"option value"');k[c]=!0;n.$viewValue==c&&(a.val(c),p.parent()&&p.remove());d[0].hasAttribute("selected")&&(d[0].selected=!0)};m.removeOption=function(a){this.hasOption(a)&&(delete k[a],n.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Ka(c)+" ?";p.val(c);a.prepend(p);a.val(c);p.prop("selected",
!0)};m.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){m.renderUnknownOption=w})}],link:function(e,g,h,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(y.parent()&&y.remove(),c.val(a),""===a&&w.prop("selected",!0)):F(a)&&w?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){y.parent()&&y.remove();d.$setViewValue(c.val())})})}function n(a,c,d){var e;d.$render=function(){var a=new Xa(d.$viewValue);r(c.find("option"),
function(c){c.selected=B(a.get(c.value))})};a.$watch(function(){ra(e,d.$viewValue)||(e=qa(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function p(e,f,h){function g(){z||(e.$$postDigest(k),z=!0)}function k(){z=!1;var a={"":[]},c=[""],d,g,l,s,t;l=h.$modelValue;s=F(e)||[];var A=p?Zb(s):s,G,y,D;y={};D=!1;if(q)if(g=h.$modelValue,x&&L(g))for(D=new Xa([]),d={},t=0;t<g.length;t++)d[n]=
g[t],D.put(x(e,d),g[t]);else D=new Xa(g);t=D;var H,K;for(D=0;G=A.length,D<G;D++){g=D;if(p){g=A[D];if("$"===g.charAt(0))continue;y[p]=g}y[n]=s[g];d=r(e,y)||"";(g=a[d])||(g=a[d]=[],c.push(d));q?d=B(t.remove(x?x(e,y):w(e,y))):(x?(d={},d[n]=l,d=x(e,d)===x(e,y)):d=l===w(e,y),t=t||d);H=m(e,y);H=B(H)?H:"";g.push({id:x?x(e,y):p?A[D]:D,label:H,selected:d})}q||(u||null===l?a[""].unshift({id:"",label:"",selected:!t}):t||a[""].unshift({id:"?",label:"",selected:!0}));y=0;for(A=c.length;y<A;y++){d=c[y];g=a[d];
E.length<=y?(l={element:v.clone().attr("label",d),label:g.label},s=[l],E.push(s),f.append(l.element)):(s=E[y],l=s[0],l.label!=d&&l.element.attr("label",l.label=d));H=null;D=0;for(G=g.length;D<G;D++)d=g[D],(t=s[D+1])?(H=t.element,t.label!==d.label&&H.text(t.label=d.label),t.id!==d.id&&H.val(t.id=d.id),H[0].selected!==d.selected&&(H.prop("selected",t.selected=d.selected),X&&H.prop("selected",t.selected))):(""===d.id&&u?K=u:(K=C.clone()).val(d.id).prop("selected",d.selected).attr("selected",d.selected).text(d.label),
s.push({element:K,label:d.label,id:d.id,selected:d.selected}),H?H.after(K):l.element.append(K),H=K);for(D++;s.length>D;)s.pop().element.remove()}for(;E.length>y;)E.pop()[0].element.remove()}var l;if(!(l=t.match(d)))throw Hf("iexp",t,ta(f));var m=c(l[2]||l[1]),n=l[4]||l[6],p=l[5],r=c(l[3]||""),w=c(l[2]?l[1]:n),F=c(l[7]),x=l[8]?c(l[8]):null,E=[[{element:f,label:""}]];u&&(a(u)(e),u.removeClass("ng-scope"),u.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=F(e)||[],d={},g,l,m,
r,t,u,v;if(q)for(l=[],r=0,u=E.length;r<u;r++)for(a=E[r],m=1,t=a.length;m<t;m++){if((g=a[m].element)[0].selected){g=g.val();p&&(d[p]=g);if(x)for(v=0;v<c.length&&(d[n]=c[v],x(e,d)!=g);v++);else d[n]=c[g];l.push(w(e,d))}}else if(g=f.val(),"?"==g)l=s;else if(""===g)l=null;else if(x)for(v=0;v<c.length;v++){if(d[n]=c[v],x(e,d)==g){l=w(e,d);break}}else d[n]=c[g],p&&(d[p]=g),l=w(e,d);h.$setViewValue(l);k()})});h.$render=k;e.$watchCollection(F,g);q&&e.$watchCollection(function(){return h.$modelValue},g)}if(m[1]){var l=
m[0];m=m[1];var q=h.multiple,t=h.ngOptions,u=!1,w,z=!1,C=G(Y.createElement("option")),v=G(Y.createElement("optgroup")),y=C.clone();h=0;for(var E=g.children(),D=E.length;h<D;h++)if(""===E[h].value){w=u=E.eq(h);break}l.init(m,u,y);q&&(m.$isEmpty=function(a){return!a||0===a.length});t?p(e,g,m):q?n(e,g,m):k(e,g,m,l)}}}}],Kd=["$interpolate",function(a){var c={addOption:w,removeOption:w};return{restrict:"E",priority:100,compile:function(d,e){if(F(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,
d,e){var k=d.parent(),n=k.data("$selectController")||k.parent().data("$selectController");n&&n.databound?d.prop("selected",!1):n=c;f?a.$watch(f,function(a,c){e.$set("value",a);c!==a&&n.removeOption(c);n.addOption(a,d)}):n.addOption(e.value,d);d.on("$destroy",function(){n.removeOption(e.value)})}}}}],Jd=da({restrict:"E",terminal:!1});t.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(zd(),Bd(Ea),G(Y).ready(function(){vd(Y,gc)}))})(window,document);
!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-animate){display:none !important;}ng\\:form{display:block;}</style>');
//# sourceMappingURL=angular.min.js.map //# sourceMappingURL=angular.min.js.map

117
webclient/js/autofill-event.js Executable file
View file

@ -0,0 +1,117 @@
/**
* Autofill event polyfill ##version:1.0.0##
* (c) 2014 Google, Inc.
* License: MIT
*/
(function(window) {
var $ = window.jQuery || window.angular.element;
var rootElement = window.document.documentElement,
$rootElement = $(rootElement);
addGlobalEventListener('change', markValue);
addValueChangeByJsListener(markValue);
$.prototype.checkAndTriggerAutoFillEvent = jqCheckAndTriggerAutoFillEvent;
// Need to use blur and not change event
// as Chrome does not fire change events in all cases an input is changed
// (e.g. when starting to type and then finish the input by auto filling a username)
addGlobalEventListener('blur', function(target) {
// setTimeout needed for Chrome as it fills other
// form fields a little later...
window.setTimeout(function() {
findParentForm(target).find('input').checkAndTriggerAutoFillEvent();
}, 20);
});
window.document.addEventListener('DOMContentLoaded', function() {
// The timeout is needed for Chrome as it auto fills
// login forms some time after DOMContentLoaded!
window.setTimeout(function() {
$rootElement.find('input').checkAndTriggerAutoFillEvent();
}, 200);
}, false);
return;
// ----------
function jqCheckAndTriggerAutoFillEvent() {
var i, el;
for (i=0; i<this.length; i++) {
el = this[i];
if (!valueMarked(el)) {
markValue(el);
triggerChangeEvent(el);
}
}
}
function valueMarked(el) {
var val = el.value,
$$currentValue = el.$$currentValue;
if (!val && !$$currentValue) {
return true;
}
return val === $$currentValue;
}
function markValue(el) {
el.$$currentValue = el.value;
}
function addValueChangeByJsListener(listener) {
var jq = window.jQuery || window.angular.element,
jqProto = jq.prototype;
var _val = jqProto.val;
jqProto.val = function(newValue) {
var res = _val.apply(this, arguments);
if (arguments.length > 0) {
forEach(this, function(el) {
listener(el, newValue);
});
}
return res;
}
}
function addGlobalEventListener(eventName, listener) {
// Use a capturing event listener so that
// we also get the event when it's stopped!
// Also, the blur event does not bubble.
rootElement.addEventListener(eventName, onEvent, true);
function onEvent(event) {
var target = event.target;
listener(target);
}
}
function findParentForm(el) {
while (el) {
if (el.nodeName === 'FORM') {
return $(el);
}
el = el.parentNode;
}
return $();
}
function forEach(arr, listener) {
if (arr.forEach) {
return arr.forEach(listener);
}
var i;
for (i=0; i<arr.length; i++) {
listener(arr[i]);
}
}
function triggerChangeEvent(element) {
var doc = window.document;
var event = doc.createEvent("HTMLEvents");
event.initEvent("change", true, true);
element.dispatchEvent(event);
}
})(window);

View file

@ -27,12 +27,30 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand
// $rootScope of the parent where the recents component is included can override this value // $rootScope of the parent where the recents component is included can override this value
// in order to highlight a specific room in the list // in order to highlight a specific room in the list
$rootScope.recentsSelectedRoomID; $rootScope.recentsSelectedRoomID;
var listenToEventStream = function() { var listenToEventStream = function() {
// Refresh the list on matrix invitation and message event // Refresh the list on matrix invitation and message event
$rootScope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { $rootScope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
if (isLive) { if (isLive) {
$rootScope.rooms[event.room_id].lastMsg = event; if (!$rootScope.rooms[event.room_id]) {
// The user has joined a new room, which we do not have data yet. The reason is that
// the room has appeared in the scope of the user rooms after the global initialSync
// FIXME: an initialSync on this specific room should be done
$rootScope.rooms[event.room_id] = {
room_id:event.room_id
};
}
else if (event.state_key === matrixService.config().user_id && "invite" !== event.membership && "join" !== event.membership) {
// The user has been kicked or banned from the room, remove this room from the recents
delete $rootScope.rooms[event.room_id];
}
if ($rootScope.rooms[event.room_id]) {
$rootScope.rooms[event.room_id].lastMsg = event;
}
// Update room users count
$rootScope.rooms[event.room_id].numUsersInRoom = getUsersCountInRoom(event.room_id);
} }
}); });
$rootScope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) { $rootScope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) {
@ -50,15 +68,48 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand
$rootScope.rooms[event.room_id] = event; $rootScope.rooms[event.room_id] = event;
} }
}); });
$rootScope.$on(eventHandlerService.NAME_EVENT, function(ngEvent, event, isLive) {
if (isLive) {
$rootScope.rooms[event.room_id].lastMsg = event;
}
});
$rootScope.$on(eventHandlerService.TOPIC_EVENT, function(ngEvent, event, isLive) {
if (isLive) {
$rootScope.rooms[event.room_id].lastMsg = event;
}
});
}; };
/**
* Compute the room users number, ie the number of members who has joined the room.
* @param {String} room_id the room id
* @returns {undefined | Number} the room users number if available
*/
var getUsersCountInRoom = function(room_id) {
var memberCount;
var room = $rootScope.events.rooms[room_id];
if (room) {
memberCount = 0;
for (var i in room.members) {
var member = room.members[i];
if ("join" === member.membership) {
memberCount = memberCount + 1;
}
}
}
return memberCount;
};
$scope.onInit = function() { $scope.onInit = function() {
// Init recents list only once // Init recents list only once
if ($rootScope.rooms) { if ($rootScope.rooms) {
return; return;
} }
$rootScope.rooms = {}; $rootScope.rooms = {};
// Use initialSync data to init the recents list // Use initialSync data to init the recents list
@ -76,11 +127,8 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand
if (room.messages && room.messages.chunk && room.messages.chunk[0]) { if (room.messages && room.messages.chunk && room.messages.chunk[0]) {
$rootScope.rooms[room.room_id].lastMsg = room.messages.chunk[0]; $rootScope.rooms[room.room_id].lastMsg = room.messages.chunk[0];
} }
}
$rootScope.rooms[room.room_id].numUsersInRoom = getUsersCountInRoom(room.room_id);
var presence = initialSyncData.data.presence;
for (var i = 0; i < presence.length; ++i) {
eventHandlerService.handleEvent(presence[i], false);
} }
// From now, update recents from the stream // From now, update recents from the stream
@ -91,6 +139,11 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand
} }
); );
}; };
// Clean data when user logs out
$scope.$on(eventHandlerService.RESET_EVENT, function() {
delete $rootScope.rooms;
});
}]); }]);

View file

@ -8,13 +8,18 @@
<td class="recentsRoomName"> <td class="recentsRoomName">
{{ room.room_id | mRoomName }} {{ room.room_id | mRoomName }}
</td> </td>
<td class="recentsRoomSummaryTS">
<span ng-show="undefined !== room.numUsersInRoom">
{{ room.numUsersInRoom || '1' }} {{ room.numUsersInRoom == 1 ? 'user' : 'users' }}
</span>
</td>
<td class="recentsRoomSummaryTS"> <td class="recentsRoomSummaryTS">
{{ (room.lastMsg.ts) | date:'MMM d HH:mm' }} {{ (room.lastMsg.ts) | date:'MMM d HH:mm' }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="2" class="recentsRoomSummary"> <td colspan="3" class="recentsRoomSummary">
<div ng-show="room.membership === 'invite'"> <div ng-show="room.membership === 'invite'">
{{ room.lastMsg.inviter | mUserDisplayName: room.room_id }} invited you {{ room.lastMsg.inviter | mUserDisplayName: room.room_id }} invited you
@ -61,7 +66,7 @@
</div> </div>
<div ng-switch-when="m.emote"> <div ng-switch-when="m.emote">
<span ng-bind-html="'* ' + (room.lastMsg.user_id) + ' ' + room.lastMsg.content.body | linky:'_blank'"> <span ng-bind-html="'* ' + (room.lastMsg.user_id | mUserDisplayName: room.room_id) + ' ' + room.lastMsg.content.body | linky:'_blank'">
</span> </span>
</div> </div>
@ -71,6 +76,14 @@
</div> </div>
</div> </div>
<div ng-switch-when="m.room.topic">
{{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the topic to: {{ room.lastMsg.content.topic }}
</div>
<div ng-switch-when="m.room.name">
{{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the room name to: {{ room.lastMsg.content.name }}
</div>
<div ng-switch-default> <div ng-switch-default>
<div ng-if="room.lastMsg.type.indexOf('m.call.') === 0"> <div ng-if="room.lastMsg.type.indexOf('m.call.') === 0">
Call Call

View file

@ -15,8 +15,8 @@ limitations under the License.
*/ */
angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
.controller('RoomController', ['$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'mPresence', 'matrixPhoneService', 'MatrixCall', .controller('RoomController', ['$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'mPresence', 'matrixPhoneService', 'MatrixCall',
function($scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, mPresence, matrixPhoneService, MatrixCall) { function($filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, mPresence, matrixPhoneService, MatrixCall) {
'use strict'; 'use strict';
var MESSAGES_PER_PAGINATION = 30; var MESSAGES_PER_PAGINATION = 30;
var THUMBNAIL_SIZE = 320; var THUMBNAIL_SIZE = 320;
@ -27,12 +27,12 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.state = { $scope.state = {
user_id: matrixService.config().user_id, user_id: matrixService.config().user_id,
events_from: "END", // when to start the event stream from. permission_denied: undefined, // If defined, this string contains the reason why the user cannot join the room
earliest_token: "END", // stores how far back we've paginated.
first_pagination: true, // this is toggled off when the first pagination is done first_pagination: true, // this is toggled off when the first pagination is done
can_paginate: true, // this is toggled off when we run out of items can_paginate: false, // this is toggled off when we are not ready yet to paginate or when we run out of items
paginating: false, // used to avoid concurrent pagination requests pulling in dup contents paginating: false, // used to avoid concurrent pagination requests pulling in dup contents
stream_failure: undefined // the response when the stream fails stream_failure: undefined, // the response when the stream fails
waiting_for_joined_event: false // true when the join request is pending. Back to false once the corresponding m.room.member event is received
}; };
$scope.members = {}; $scope.members = {};
$scope.autoCompleting = false; $scope.autoCompleting = false;
@ -42,6 +42,85 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.imageURLToSend = ""; $scope.imageURLToSend = "";
$scope.userIDToInvite = ""; $scope.userIDToInvite = "";
// vars and functions for updating the name
$scope.name = {
isEditing: false,
newNameText: "",
editName: function() {
if ($scope.name.isEditing) {
console.log("Warning: Already editing name.");
return;
};
// Use the filter applied in html to set the input value
$scope.name.newNameText = $filter('mRoomName')($scope.room_id);
// Force focus to the input
$timeout(function() {
angular.element('.roomNameInput').focus();
}, 0);
$scope.name.isEditing = true;
},
updateName: function() {
console.log("Updating name to "+$scope.name.newNameText);
matrixService.setName($scope.room_id, $scope.name.newNameText).then(
function() {
},
function(error) {
$scope.feedback = "Request failed: " + error.data.error;
}
);
$scope.name.isEditing = false;
},
cancelEdit: function() {
$scope.name.isEditing = false;
}
};
// vars and functions for updating the topic
$scope.topic = {
isEditing: false,
newTopicText: "",
editTopic: function() {
if ($scope.topic.isEditing) {
console.log("Warning: Already editing topic.");
return;
}
var topicEvent = $rootScope.events.rooms[$scope.room_id]['m.room.topic'];
if (topicEvent) {
$scope.topic.newTopicText = topicEvent.content.topic;
}
else {
$scope.topic.newTopicText = "";
}
// Force focus to the input
$timeout(function() {
angular.element('.roomTopicInput').focus();
}, 0);
$scope.topic.isEditing = true;
},
updateTopic: function() {
console.log("Updating topic to "+$scope.topic.newTopicText);
matrixService.setTopic($scope.room_id, $scope.topic.newTopicText).then(
function() {
},
function(error) {
$scope.feedback = "Request failed: " + error.data.error;
}
);
$scope.topic.isEditing = false;
},
cancelEdit: function() {
$scope.topic.isEditing = false;
}
};
var scrollToBottom = function(force) { var scrollToBottom = function(force) {
console.log("Scrolling to bottom"); console.log("Scrolling to bottom");
@ -81,8 +160,37 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
$scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
if (isLive) { if (isLive) {
scrollToBottom(); if ($scope.state.waiting_for_joined_event) {
updateMemberList(event); // The user has successfully joined the room, we can getting data for this room
$scope.state.waiting_for_joined_event = false;
onInit3();
}
else if (event.state_key === $scope.state.user_id && "invite" !== event.membership && "join" !== event.membership) {
var user;
if ($scope.members[event.user_id]) {
user = $scope.members[event.user_id].displayname;
}
if (user) {
user = user + " (" + event.user_id + ")";
}
else {
user = event.user_id;
}
if ("ban" === event.membership) {
$scope.state.permission_denied = "You have been banned by " + user;
}
else {
$scope.state.permission_denied = "You have been kicked by " + user;
}
}
else {
scrollToBottom();
updateMemberList(event);
}
} }
}); });
@ -112,19 +220,22 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
}; };
var paginate = function(numItems) { var paginate = function(numItems) {
// console.log("paginate " + numItems); //console.log("paginate " + numItems + " and first_pagination is " + $scope.state.first_pagination);
if ($scope.state.paginating || !$scope.room_id) { if ($scope.state.paginating || !$scope.room_id) {
return; return;
} }
else { else {
$scope.state.paginating = true; $scope.state.paginating = true;
} }
// console.log("paginateBackMessages from " + $scope.state.earliest_token + " for " + numItems);
console.log("paginateBackMessages from " + $rootScope.events.rooms[$scope.room_id].pagination.earliest_token + " for " + numItems);
var originalTopRow = $("#messageTable>tbody>tr:first")[0]; var originalTopRow = $("#messageTable>tbody>tr:first")[0];
matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then(
// Paginate events from the point in cache
matrixService.paginateBackMessages($scope.room_id, $rootScope.events.rooms[$scope.room_id].pagination.earliest_token, numItems).then(
function(response) { function(response) {
eventHandlerService.handleEvents(response.data.chunk, false);
$scope.state.earliest_token = response.data.end; eventHandlerService.handleRoomMessages($scope.room_id, response.data, false);
if (response.data.chunk.length < MESSAGES_PER_PAGINATION) { if (response.data.chunk.length < MESSAGES_PER_PAGINATION) {
// no more messages to paginate. this currently never gets turned true again, as we never // no more messages to paginate. this currently never gets turned true again, as we never
// expire paginated contents in the current implementation. // expire paginated contents in the current implementation.
@ -149,7 +260,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
} }
if ($scope.state.first_pagination) { if ($scope.state.first_pagination) {
scrollToBottom(); scrollToBottom(true);
$scope.state.first_pagination = false; $scope.state.first_pagination = false;
} }
else { else {
@ -419,7 +530,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var powerLevel = 50; // default power level for op var powerLevel = 50; // default power level for op
if (matches) { if (matches) {
var user_id = matches[1]; var user_id = matches[1];
if (matches.length === 4) { if (matches.length === 4 && undefined !== matches[3]) {
powerLevel = parseInt(matches[3]); powerLevel = parseInt(matches[3]);
} }
if (powerLevel !== NaN) { if (powerLevel !== NaN) {
@ -472,8 +583,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
room_id: $scope.room_id, room_id: $scope.room_id,
type: "m.room.message", type: "m.room.message",
user_id: $scope.state.user_id, user_id: $scope.state.user_id,
// FIXME: re-enable echo_msg_state when we have a nice way to turn the field off again echo_msg_state: "messagePending" // Add custom field to indicate the state of this fake message to HTML
// echo_msg_state: "messagePending" // Add custom field to indicate the state of this fake message to HTML
}; };
$scope.textInput = ""; $scope.textInput = "";
@ -482,26 +592,22 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
} }
if (promise) { if (promise) {
// Reset previous feedback
$scope.feedback = "";
promise.then( promise.then(
function() { function(response) {
console.log("Request successfully sent"); console.log("Request successfully sent");
if (!echo) {
$scope.textInput = ""; if (echo) {
} // Mark this fake message event with its allocated event_id
/* // When the true message event will come from the events stream (in handleMessage),
if (echoMessage) { // we will be able to replace the fake one by the true one
// Remove the fake echo message from the room messages echoMessage.event_id = response.data.event_id;
// It will be replaced by the one acknowledged by the server
// ...except this causes a nasty flicker. So don't swap messages for now. --matthew
// var index = $rootScope.events.rooms[$scope.room_id].messages.indexOf(echoMessage);
// if (index > -1) {
// $rootScope.events.rooms[$scope.room_id].messages.splice(index, 1);
// }
} }
else { else {
$scope.textInput = ""; $scope.textInput = "";
} }
*/
}, },
function(error) { function(error) {
$scope.feedback = "Request failed: " + error.data.error; $scope.feedback = "Request failed: " + error.data.error;
@ -596,14 +702,18 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
// Do we to join the room before starting? // Do we to join the room before starting?
if (needsToJoin) { if (needsToJoin) {
$scope.state.waiting_for_joined_event = true;
matrixService.join($scope.room_id).then( matrixService.join($scope.room_id).then(
function() { function() {
// onInit3 will be called once the joined m.room.member event is received from the events stream
// This avoids to get the joined information twice in parallel:
// - one from the events stream
// - one from the pagination because the pagination window covers this event ts
console.log("Joined room "+$scope.room_id); console.log("Joined room "+$scope.room_id);
onInit3();
}, },
function(reason) { function(reason) {
console.log("Can't join room: " + JSON.stringify(reason)); console.log("Can't join room: " + JSON.stringify(reason));
$scope.feedback = "You do not have permission to join this room"; $scope.state.permission_denied = "You do not have permission to join this room";
}); });
} }
else { else {
@ -615,9 +725,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var onInit3 = function() { var onInit3 = function() {
console.log("onInit3"); console.log("onInit3");
// TODO: We should be able to keep them
eventHandlerService.resetRoomMessages($scope.room_id);
// Make recents highlight the current room // Make recents highlight the current room
$scope.recentsSelectedRoomID = $scope.room_id; $scope.recentsSelectedRoomID = $scope.room_id;
@ -635,21 +742,24 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
// Arm list timing update timer // Arm list timing update timer
updateMemberListPresenceAge(); updateMemberListPresenceAge();
// Start pagination
$scope.state.can_paginate = true;
paginate(MESSAGES_PER_PAGINATION);
}, },
function(error) { function(error) {
$scope.feedback = "Failed get member list: " + error.data.error; $scope.feedback = "Failed get member list: " + error.data.error;
} }
); );
paginate(MESSAGES_PER_PAGINATION);
}; };
$scope.inviteUser = function(user_id) { $scope.inviteUser = function() {
matrixService.invite($scope.room_id, user_id).then( matrixService.invite($scope.room_id, $scope.userIDToInvite).then(
function() { function() {
console.log("Invited."); console.log("Invited.");
$scope.feedback = "Invite sent successfully"; $scope.feedback = "Invite successfully sent to " + $scope.userIDToInvite;
$scope.userIDToInvite = "";
}, },
function(reason) { function(reason) {
$scope.feedback = "Failure: " + reason; $scope.feedback = "Failure: " + reason;
@ -710,7 +820,15 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
var call = new MatrixCall($scope.room_id); var call = new MatrixCall($scope.room_id);
call.onError = $rootScope.onCallError; call.onError = $rootScope.onCallError;
call.onHangup = $rootScope.onCallHangup; call.onHangup = $rootScope.onCallHangup;
call.placeCall(); call.placeCall({audio: true, video: false});
$rootScope.currentCall = call;
};
$scope.startVideoCall = function() {
var call = new MatrixCall($scope.room_id);
call.onError = $rootScope.onCallError;
call.onHangup = $rootScope.onCallHangup;
call.placeCall({audio: true, video: true});
$rootScope.currentCall = call; $rootScope.currentCall = call;
}; };

View file

@ -2,8 +2,31 @@
<div id="roomHeader"> <div id="roomHeader">
<a href ng-click="goToPage('/')"><img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/></a> <a href ng-click="goToPage('/')"><img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/></a>
<div id="roomName"> <div class="roomHeaderInfo">
{{ room_id | mRoomName }}
<div class="roomNameSection">
<div ng-hide="name.isEditing" ng-dblclick="name.editName()" id="roomName">
{{ room_id | mRoomName }}
</div>
<form ng-submit="name.updateName()" ng-show="name.isEditing" class="roomNameForm">
<input ng-model="name.newNameText" ng-blur="name.cancelEdit()" class="roomNameInput" />
</form>
</div>
<div class="roomTopicSection">
<button ng-hide="events.rooms[room_id]['m.room.topic'].content.topic || topic.isEditing"
ng-click="topic.editTopic()" class="roomTopicSetNew">
Set Topic
</button>
<div ng-show="events.rooms[room_id]['m.room.topic'].content.topic || topic.isEditing">
<div ng-hide="topic.isEditing" ng-dblclick="topic.editTopic()" id="roomTopic">
{{ events.rooms[room_id]['m.room.topic'].content.topic | limitTo: 200}}
</div>
<form ng-submit="topic.updateTopic()" ng-show="topic.isEditing" class="roomTopicForm">
<input ng-model="topic.newTopicText" ng-blur="topic.cancelEdit()" class="roomTopicInput" />
</form>
</div>
</div>
</div> </div>
</div> </div>
@ -14,14 +37,14 @@
<div ng-include="'recents/recents.html'"></div> <div ng-include="'recents/recents.html'"></div>
</div> </div>
<div id="usersTableWrapper"> <div id="usersTableWrapper" ng-hide="state.permission_denied">
<table id="usersTable"> <table id="usersTable">
<tr ng-repeat="member in members | orderMembersList"> <tr ng-repeat="member in members | orderMembersList">
<td class="userAvatar mouse-pointer" ng-click="$parent.goToUserPage(member.id)" ng-class="member.membership == 'invite' ? 'invited' : ''"> <td class="userAvatar mouse-pointer" ng-click="$parent.goToUserPage(member.id)" ng-class="member.membership == 'invite' ? 'invited' : ''">
<img class="userAvatarImage" <img class="userAvatarImage"
ng-src="{{member.avatar_url || 'img/default-profile.png'}}" ng-src="{{member.avatar_url || 'img/default-profile.png'}}"
alt="{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}" alt="{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}"
title="{{ member.id }}" title="{{ member.id }} - power: {{ member.powerLevel }}"
width="80" height="80"/> width="80" height="80"/>
<img class="userAvatarGradient" src="img/gradient.png" title="{{ member.id }}" width="80" height="24"/> <img class="userAvatarGradient" src="img/gradient.png" title="{{ member.id }}" width="80" height="24"/>
<div class="userPowerLevel" ng-style="{'width': member.powerLevelNorm +'%'}"></div> <div class="userPowerLevel" ng-style="{'width': member.powerLevelNorm +'%'}"></div>
@ -33,7 +56,7 @@
</table> </table>
</div> </div>
<div id="messageTableWrapper" keep-scroll> <div id="messageTableWrapper" ng-hide="state.permission_denied" keep-scroll>
<!-- FIXME: need to have better timestamp semantics than the (msg.content.hsob_ts || msg.ts) hack below --> <!-- FIXME: need to have better timestamp semantics than the (msg.content.hsob_ts || msg.ts) hack below -->
<table id="messageTable" infinite-scroll="paginateMore()"> <table id="messageTable" infinite-scroll="paginateMore()">
<tr ng-repeat="msg in events.rooms[room_id].messages" <tr ng-repeat="msg in events.rooms[room_id].messages"
@ -49,7 +72,7 @@
<img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32" <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32"
ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/> ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/>
</td> </td>
<td ng-class="!msg.content.membership ? (msg.content.msgtype === 'm.emote' ? 'emote text' : 'text') : 'membership text'"> <td ng-class="(!msg.content.membership && ('m.room.topic' !== msg.type && 'm.room.name' !== msg.type))? (msg.content.msgtype === 'm.emote' ? 'emote text' : 'text') : 'membership text'">
<div class="bubble"> <div class="bubble">
<span ng-if="'join' === msg.content.membership"> <span ng-if="'join' === msg.content.membership">
{{ members[msg.state_key].displayname || msg.state_key }} joined {{ members[msg.state_key].displayname || msg.state_key }} joined
@ -76,10 +99,18 @@
</span> </span>
</span> </span>
<span ng-show='msg.content.msgtype === "m.emote"' ng-bind-html="'* ' + (members[msg.user_id].displayname || msg.user_id) + ' ' + msg.content.body | linky:'_blank'"/> <span ng-show='msg.content.msgtype === "m.emote"'
ng-class="msg.echo_msg_state"
ng-bind-html="'* ' + (members[msg.user_id].displayname || msg.user_id) + ' ' + msg.content.body | linky:'_blank'"/>
<span ng-show='msg.content.msgtype === "m.text"' <span ng-show='msg.content.msgtype === "m.text"'
class="message"
ng-class="msg.echo_msg_state" ng-class="msg.echo_msg_state"
ng-bind-html="((msg.content.msgtype === 'm.text') ? msg.content.body : '') | linky:'_blank'"/> ng-bind-html="((msg.content.msgtype === 'm.text') ? msg.content.body : '') | linky:'_blank'"/>
<span ng-show='msg.type === "m.call.invite" && msg.user_id == state.user_id'>Outgoing Call</span>
<span ng-show='msg.type === "m.call.invite" && msg.user_id != state.user_id'>Incoming Call</span>
<div ng-show='msg.content.msgtype === "m.image"'> <div ng-show='msg.content.msgtype === "m.image"'>
<div ng-hide='msg.content.thumbnail_url' ng-style="msg.content.body.h && { 'height' : (msg.content.body.h < 320) ? msg.content.body.h : 320}"> <div ng-hide='msg.content.thumbnail_url' ng-style="msg.content.body.h && { 'height' : (msg.content.body.h < 320) ? msg.content.body.h : 320}">
<img class="image" ng-src="{{ msg.content.url }}"/> <img class="image" ng-src="{{ msg.content.url }}"/>
@ -89,6 +120,15 @@
ng-click="$parent.fullScreenImageURL = msg.content.url"/> ng-click="$parent.fullScreenImageURL = msg.content.url"/>
</div> </div>
</div> </div>
<span ng-if="'m.room.topic' === msg.type">
{{ members[msg.user_id].displayname || msg.user_id }} changed the topic to: {{ msg.content.topic }}
</span>
<span ng-if="'m.room.name' === msg.type">
{{ members[msg.user_id].displayname || msg.user_id }} changed the room name to: {{ msg.content.name }}
</span>
</div> </div>
</td> </td>
<td class="rightBlock"> <td class="rightBlock">
@ -98,6 +138,10 @@
</tr> </tr>
</table> </table>
</div> </div>
<div ng-show="state.permission_denied">
{{ state.permission_denied }}
</div>
</div> </div>
</div> </div>
@ -110,11 +154,13 @@
{{ state.user_id }} {{ state.user_id }}
</td> </td>
<td width="*"> <td width="*">
<input id="mainInput" ng-model="textInput" ng-enter="send()" ng-focus="true" autocomplete="off" tab-complete/> <textarea id="mainInput" rows="1" ng-model="textInput" ng-enter="send()"
ng-disabled="state.permission_denied"
ng-focus="true" autocomplete="off" tab-complete/>
</td> </td>
<td id="buttonsCell"> <td id="buttonsCell">
<button ng-click="send()">Send</button> <button ng-click="send()" ng-disabled="state.permission_denied">Send</button>
<button m-file-input="imageFileToSend" class="extraControls">Image</button> <button m-file-input="imageFileToSend" class="extraControls" ng-disabled="state.permission_denied">Image</button>
</td> </td>
</tr> </tr>
</table> </table>
@ -122,11 +168,11 @@
<div class="extraControls"> <div class="extraControls">
<span> <span>
Invite a user: Invite a user:
<input ng-model="userIDToInvite" size="32" type="text" placeholder="User ID (ex:@user:homeserver)"/> <input ng-model="userIDToInvite" size="32" type="text" ng-enter="inviteUser()" ng-disabled="state.permission_denied" placeholder="User ID (ex:@user:homeserver)"/>
<button ng-click="inviteUser(userIDToInvite)">Invite</button> <button ng-click="inviteUser()" ng-disabled="state.permission_denied">Invite</button>
</span> </span>
<button ng-click="leaveRoom()">Leave</button> <button ng-click="leaveRoom()" ng-disabled="state.permission_denied">Leave</button>
<button ng-click="startVoiceCall()" ng-show="(currentCall == undefined || currentCall.state == 'ended') && memberCount() == 2">Voice Call</button> <button ng-click="startVoiceCall()" ng-show="(currentCall == undefined || currentCall.state == 'ended') && memberCount() == 2" ng-disabled="state.permission_denied">Voice Call</button>
</div> </div>
{{ feedback }} {{ feedback }}