part of angular.core.parser;
class Lexer {
static const String QUOTES = "\"'";
static const String DOT = ".";
static const String SPECIAL = "(){}[].,;:";
static const String JSON_SEP = "{,";
static const String JSON_OPEN = "{[";
static const String JSON_CLOSE = "}]";
static const String WHITESPACE = " \r\t\n\v\u00A0";
static const String EXP_OP = "Ee";
static const String SIGN_OP = "+-";
static Map<String, String> ESCAPE =
{"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
List<Token> call(String text) {
List<Token> tokens = [];
Token token;
int index = 0;
int lastIndex;
int textLength = text.length;
String ch;
String lastCh = ":";
isIn(String charSet, [String c]) =>
charSet.indexOf(c != null ? c : ch) != -1;
was(String charSet) => charSet.indexOf(lastCh) != -1;
cc(String s) => s.codeUnitAt(0);
bool isNumber([String c]) {
int cch = cc(c != null ? c : ch);
return cc('0') <= cch && cch <= cc('9');
}
isIdent() {
int cch = cc(ch);
return
cc('a') <= cch && cch <= cc('z') ||
cc('A') <= cch && cch <= cc('Z') ||
cc('_') == cch || cch == cc('\$');
}
isWhitespace([String c]) => isIn(WHITESPACE, c);
isExpOperator([String c]) => isIn(SIGN_OP, c) || isNumber(c);
String peek() => index + 1 < textLength ? text[index + 1] : "EOF";
lexError(String s) { throw "Lexer Error: $s at column $index in expression [$text]"; }
// whileChars takes two functions: One called for each character
// and a second, optional function call at the end of the file.
// If the first function returns false, the the loop stops and endFn
// is not run.
whileChars(fn(), [endFn()]) {
while (index < textLength) {
ch = text[index];
int lastIndex = index;
if (fn() == false) {
return;
}
if (lastIndex >= index) {
throw "while chars loop must advance at index $index";
}
}
if (endFn != null) { endFn(); }
}
readString() {
int start = index;
String string = "";
String rawString = ch;
String quote = ch;
index++;
whileChars(() {
rawString += ch;
if (ch == '\\') {
index++;
whileChars(() {
rawString += ch;
if (ch == 'u') {
String hex = text.substring(index + 1, index + 5);
int charCode = int.parse(hex, radix: 16,
onError: (s) { lexError('Invalid unicode escape [\\u$hex]'); });
string += new String.fromCharCode(charCode);
index += 5;
} else {
var rep = ESCAPE[ch];
if (rep != null) {
string += rep;
} else {
string += ch;
}
index++;
}
return false; // BREAK
});
} else if (ch == quote) {
index++;
tokens.add(new Token(start, rawString)
..withValue(string));
return false; // BREAK
} else {
string += ch;
index++;
}
}, () {
lexError('Unterminated quote starting at $start');
});
}
readNumber() {
String number = "";
int start = index;
bool simpleInt = true;
whileChars(() {
if (ch == '.') {
number += ch;
simpleInt = false;
} else if (isNumber()) {
number += ch;
} else {
String peekCh = peek();
if (isIn(EXP_OP) && isExpOperator(peekCh)) {
simpleInt = false;
number += ch;
} else if (isExpOperator() && peekCh != '' && isNumber(peekCh) && isIn(EXP_OP, number[number.length - 1])) {
simpleInt = false;
number += ch;
} else if (isExpOperator() && (peekCh == '' || !isNumber(peekCh)) &&
isIn(EXP_OP, number[number.length - 1])) {
lexError('Invalid exponent');
} else {
return false; // BREAK
}
}
index++;
});
var ret = simpleInt ? int.parse(number) : double.parse(number);
tokens.add(new Token(start, number)..withValue(ret));
}
readIdent() {
String ident = "";
int start = index;
int lastDot = -1, peekIndex = -1;
String methodName;
whileChars(() {
if (ch == '.' || isIdent() || isNumber()) {
if (ch == '.') {
lastDot = index;
}
ident += ch;
} else {
return false; // BREAK
}
index++;
});
// The identifier had a . in the identifier
if (lastDot != -1) {
peekIndex = index;
while (peekIndex < textLength) {
String peekChar = text[peekIndex];
if (peekChar == "(") {
methodName = ident.substring(lastDot - start + 1);
ident = ident.substring(0, lastDot - start);
index = peekIndex;
}
if (isWhitespace(peekChar)) {
peekIndex++;
} else {
break;
}
}
}
var token = new Token(start, ident);
if (OPERATORS.containsKey(ident)) {
token.withOp(ident);
} else {
token.withGetterSetter(ident);
}
tokens.add(token);
if (methodName != null) {
tokens.add(new Token(lastDot, '.'));
tokens.add(new Token(lastDot + 1, methodName));
}
}
oneLexLoop() {
if (isIn(QUOTES)) {
readString();
} else if (isNumber() || isIn(DOT) && isNumber(peek())) {
readNumber();
} else if (isIdent()) {
readIdent();
} else if (isIn(SPECIAL)) {
tokens.add(new Token(index, ch));
index++;
} else if (isWhitespace()) {
index++;
} else {
// Check for two character operators (e.g. "==")
String ch2 = ch + peek();
if (OPERATORS.containsKey(ch2)) {
tokens.add(new Token(index, ch2)..withOp(ch2));
index += 2;
} else if (OPERATORS.containsKey(ch)) {
tokens.add(new Token(index, ch)..withOp(ch));
index++;
} else {
lexError('Unexpected next character [$ch]');
}
}
}
whileChars(() {
oneLexLoop();
});
return tokens;
}
}
library angular.html_parser;
import 'package:html5lib/parser.dart';
import 'package:html5lib/dom.dart';
import 'selector.dart';
import 'io.dart';
import 'common.dart';
typedef NodeVisitor(Node node);
RegExp _MUSTACHE_REGEXP = new RegExp(r'{{([^}]*)}}');
RegExp _NG_REPEAT_SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$');
class HtmlExpressionExtractor {
List<DirectiveInfo> directiveInfos;
IoService ioService;
HtmlExpressionExtractor(this.directiveInfos, IoService this.ioService);
Set<String> expressions = new Set<String>();
void crawl(root) {
ioService.visitFs(root, (String file) {
if (!file.endsWith('.html')) return;
String html = ioService.readAsStringSync(file);
var document = parse(html);
visitNodes([document], (Node node) {
if (matchesNode(node, r'[*=/{{.*}}/]')) {
node.attributes.forEach((attrName, attrValue) {
_MUSTACHE_REGEXP.allMatches(attrValue).forEach((match) {
expressions.add(match.group(1));
});
});
}
if (matchesNode(node, r':contains(/{{.*}}/)')) {
_MUSTACHE_REGEXP.allMatches(node.value).forEach((match) {
expressions.add(match.group(1));
});
}
if (matchesNode(node, r'[ng-repeat]')) {
var expr = _NG_REPEAT_SYNTAX.
firstMatch(node.attributes['ng-repeat']).group(2);
expressions.add(expr);
}
for (DirectiveInfo directiveInfo in directiveInfos) {
if (matchesNode(node, directiveInfo.selector)) {
directiveInfo.expressionAttrs.forEach((attr) {
if (node.attributes[attr] != null && attr != 'ng-repeat') {
expressions.add(node.attributes[attr]);
}
});
}
}
});
});
for (DirectiveInfo directiveInfo in directiveInfos) {
expressions.addAll(directiveInfo.expressions);
}
}
visitNodes(List<Node> nodes, NodeVisitor visitor) {
for (Node node in nodes) {
visitor(node);
if (node.nodes.length > 0) {
visitNodes(node.nodes, visitor);
}
}
}
}
part of angular.directive;
/**
* @ngdoc directive
* @name ng.directive:a
* @restrict E
*
* @description
* Modifies the default behavior of the html A tag so that the default action is prevented when
* the href attribute is empty.
*
* This change permits the easy creation of action links with the `ngClick` directive
* without changing the location or causing page reloads, e.g.:
* `<a href="" ng-click="model.$save()">Save</a>`
*/
@NgDirective(selector: 'a[href]')
class NgADirective {
dom.Element element;
NgADirective(dom.Element element) {
if (element.attributes["href"] == "") {
element.onClick.listen((event) {
if (element.attributes["href"] == "") {
event.preventDefault();
}
});
}
}
}
part of angular.core.dom;
List<dom.Node> cloneElements(elements) {
var clones = [];
for(var i = 0, ii = elements.length; i < ii; i++) {
clones.add(elements[i].clone(true));
}
return clones;
}
class DirectiveRef {
final dom.Node element;
final Type type;
final NgAnnotation annotation;
final String value;
BlockFactory blockFactory;
DirectiveRef(dom.Node this.element, Type this.type, NgAnnotation this.annotation,
[ String this.value ]);
String toString() {
var html = element is dom.Element ? (element as dom.Element).outerHtml : element.nodeValue;
return '{ element: $html, selector: ${annotation.selector}, value: $value }';
}
}
part of angular.directive;
/**
* Creates a binding that will innerHTML the result of evaluating the
* `expression` bound to `ng-bind-html` into the current element in a secure
* way. This expression must evaluate to a string. The innerHTML-ed content
* will be sanitized using a default [NodeValidator] constructed as `new
* dom.NodeValidatorBuilder.common()`. In a future version, when Strict
* Contextual Escaping support has been added to Angular.dart, this directive
* will allow one to bypass the sanitizaton and innerHTML arbitrary trusted
* HTML.
*
* Example:
*
* <div ng-bind-html="htmlVar"></div>
*/
@NgDirective(
selector: '[ng-bind-html]',
map: const {'ngBindHtml': '=.value'})
class NgBindHtmlDirective {
// The default HTML sanitizer. Eventually, we'll make this configurable or
// use an optionally loaded `$sanitize` service.
static final dom.NodeValidator validator = new dom.NodeValidatorBuilder.common();
dom.Element element;
NgBindHtmlDirective(dom.Element this.element);
/**
* Parsed expression from the `ng-bind-html` attribute. The result of this
* expression is innerHTML'd according to the rules specified in this class'
* documention.
*/
set value(value) => element.setInnerHtml((value == null ? '' : value.toString()),
validator: validator) ;
}
library mirrors;
import 'dart:mirrors';
export 'dart:mirrors';
Map<Symbol, ClassMirror> _classMirrorCache = new Map<Symbol, ClassMirror>();
const List<Symbol> PRIMITIVE_TYPES = const <Symbol>[
const Symbol('dart.core.dynamic'), const Symbol('dart.core.num'),
const Symbol('dart.core.int'), const Symbol('dart.core.double'),
const Symbol('dart.core.String'), const Symbol('dart.core.bool')
];
const Map<String, String> _PRIMITIVE_TYPE_SIMPLE_NAMES = const {
'dart.core.dynamic': 'dynamic',
'dart.core.num': 'num',
'dart.core.int': 'int',
'dart.core.double': 'double',
'dart.core.String': 'String',
'dart.core.bool': 'bool'
};
// Hack because we can't get a [ClassMirror] from a [Symbol].
ClassMirror getClassMirrorBySymbol(Symbol id) {
var mirror = _classMirrorCache[id];
if (mirror == null) {
for (var lib in currentMirrorSystem().libraries.values) {
for (ClassMirror cls in lib.classes.values) {
if (cls.qualifiedName == id) {
mirror = cls;
break;
}
}
}
_classMirrorCache[id] = mirror;
}
return mirror;
}
String getSymbolName(Symbol symbol) => MirrorSystem.getName(symbol);
String getSymbolSimpleName(Symbol symbol) {
if (PRIMITIVE_TYPES.contains(symbol)) {
return _PRIMITIVE_TYPE_SIMPLE_NAMES[getSymbolName(symbol)];
}
return MirrorSystem.getName(getClassMirrorBySymbol(symbol).simpleName);
}
Map<Type, ClassMirror> _reflectionCache = new Map<Type, ClassMirror>();
/// Cached version of [reflectClass].
ClassMirror cachedReflectClass(Type type) {
ClassMirror mirror = _reflectionCache[type];
if (mirror == null) {
mirror = reflectClass(type);
_reflectionCache[type] = mirror;
}
return mirror;
}
Symbol getTypeSymbol(Type type) => cachedReflectClass(type).qualifiedName;
part of angular.directive;
/**
* The [NgBindTemplateDirective] specifies that the element text content should be replaced with
* the interpolation of the template in the ngBindTemplate attribute. Unlike ngBind, the
* ngBindTemplate can contain multiple {{ }} expressions.
*/
@NgDirective(
selector: '[ng-bind-template]',
map: const {'ng-bind-template': '@.bind'})
class NgBindTemplateDirective {
dom.Element element;
NgBindTemplateDirective(dom.Element this.element);
set bind(value) {
element.text = value;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
if (!navigator.webkitStartDart || !navigator.webkitStartDart()) {
var message = "This example must be run in the latest build of Dartium.";
window.onload = function() { document.body.innerHTML = message; }
alert(message);
}
</script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script src="main.dart" type="application/dart"></script>
<title>test_filter_filter</title>
</head>
<body class="container"><div class="row" toy-data>
<h3>Angular.Dart: filter</h3>
Search: <input type="text" ng-model="searchText">
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:searchText">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
</tr>
</table>
<hr>
Any: <input type="text" ng-model="search.$"> <br>
Name only <input type="text" ng-model="search.name"><br>
Phone only <input type="text" ng-model="search.phone"><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:search:strict">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
</tr>
</table>
</div></body>
</html>
library angular.playback.playback_http;
import "dart:async";
import "dart:html";
import "dart:json" as json;
import "package:angular/core_dom/module.dart";
import "package:angular/mock/module.dart" as mock;
import "playback_data.dart" as playback_data;
class PlaybackHttpBackendConfig {
requestKey(String url,
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
return json.stringify({
"url": url,
"method": method,
"requestHeaders": requestHeaders,
"data": sendData
});
}
}
// HELP! The DI system is getting in the way. We want
// the HttpBackend, but it will be implemented by ourselves.
class HttpBackendWrapper {
HttpBackend backend;
HttpBackendWrapper(HttpBackend this.backend);
}
class RecordingHttpBackend implements HttpBackend {
HttpBackend _prodBackend;
PlaybackHttpBackendConfig _config;
RecordingHttpBackend(HttpBackendWrapper wrapper,
PlaybackHttpBackendConfig this._config) {
this._prodBackend = wrapper.backend;
}
Future request(String url,
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
return _prodBackend.request(url,
method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress).then((HttpRequest r) {
var key = _config.requestKey(url,
method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress);
assert(key is String);
_prodBackend.request('/record', //TODO make this URL configurable.
method: 'POST', sendData: json.stringify({
"key": key, "data": json.stringify({
"status": r.status,
"headers": r.getAllResponseHeaders(),
"data": r.responseText})
}));
return r;
});
}
}
class PlaybackHttpBackend implements HttpBackend {
PlaybackHttpBackendConfig _config;
PlaybackHttpBackend(PlaybackHttpBackendConfig this._config);
Map data = playback_data.playbackData;
Future request(String url,
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
var key = _config.requestKey(url,
method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress);
if (!data.containsKey(key)) {
throw ["Request is not recorded $key"];
}
var playback = data[key];
return new Future.value(
new mock.MockHttpRequest(
playback['status'],
playback['data'],
playback['headers']));
}
}
part of angular.core.dom;
class NodeCursor {
List<dynamic> stack = [];
List<dom.Node> elements;
num index;
NodeCursor(List<dom.Node> this.elements) {
index = 0;
}
isValid() {
return index < elements.length;
}
cursorSize() {
return 1;
}
macroNext() {
for(var i = 0, ii = cursorSize(); i < ii; i++, index++){}
return this.isValid();
}
microNext() {
var length = elements.length;
if (index < length) {
index++;
}
return index < length;
}
nodeList() {
if (!isValid()) return []; // or should we return null?
var nodes = [];
for(var i = 0, ii = cursorSize(); i < ii; i++) {
nodes.add(elements[index + i]);
}
return nodes;
}
descend() {
var childNodes = elements[index].nodes;
var hasChildren = !!(childNodes != null && childNodes.length > 0);
if (hasChildren) {
stack.add(index);
stack.add(elements);
elements = new List.from(childNodes);
index = 0;
}
return hasChildren;
}
ascend() {
elements = stack.removeLast();
index = stack.removeLast();
}
insertAnchorBefore(String name) {
var current = elements[index];
var parent = current.parentNode;
var anchor = new dom.Comment('ANCHOR: $name');
elements.insert(index++, anchor);
if (parent != null) {
parent.insertBefore(anchor, current);
}
}
replaceWithAnchor(String name) {
insertAnchorBefore(name);
var childCursor = remove();
this.index--;
return childCursor;
}
remove() {
var nodes = nodeList();
for (var i = 0, ii = nodes.length; i < ii; i++) {
// NOTE(deboer): If elements is a list of child nodes on a node, then
// calling Node.remove() may also remove it from the list. Thus, we
// call elements.removeAt first so only one node is removed.
elements.removeAt(index);
nodes[i].remove();
}
return new NodeCursor(nodes);
}
isInstance() {
return false;
}
}
part of angular.directive;
/**
* The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
*
* @example
<span ng-style="{color:'red'}">Sample Text</span>
*/
@NgDirective(
selector: '[ng-style]',
map: const { 'ng-style': '@.styleExpression'})
class NgStyleDirective {
dom.Element _element;
Scope _scope;
String _styleExpression;
NgStyleDirective(dom.Element this._element, Scope this._scope);
Function _removeWatch = () => null;
var _lastStyles;
/**
* ng-style attribute takes an expression hich evals to an
* object whose keys are CSS style names and values are corresponding values for those CSS
* keys.
*/
set styleExpression(String value) {
_styleExpression = value;
_removeWatch();
_removeWatch = _scope.$watchCollection(_styleExpression, _onStyleChange);
}
_onStyleChange(Map newStyles) {
dom.CssStyleDeclaration css = _element.style;
if (_lastStyles != null) {
_lastStyles.forEach((val, style) { css.setProperty(val, ''); });
}
_lastStyles = newStyles;
if (newStyles != null) {
newStyles.forEach((val, style) { css.setProperty(val, style); });
}
}
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library date_symbols;
/**
* This holds onto information about how a particular locale formats dates. It
* contains mostly strings, e.g. what the names of months or weekdays are,
* but also indicates things like the first day of the week. We expect the data
* for instances of these to be generated out of ICU or a similar reference
* source. This is used in conjunction with the date_time_patterns, which
* defines for a particular locale the different named formats that will
* make use of this data.
*/
class DateSymbols {
String NAME;
List<String> ERAS, ERANAMES, NARROWMONTHS, STANDALONENARROWMONTHS,
MONTHS, STANDALONEMONTHS, SHORTMONTHS, STANDALONESHORTMONTHS, WEEKDAYS,
STANDALONEWEEKDAYS, SHORTWEEKDAYS, STANDALONESHORTWEEKDAYS,
NARROWWEEKDAYS, STANDALONENARROWWEEKDAYS, SHORTQUARTERS,
QUARTERS, AMPMS, DATEFORMATS, TIMEFORMATS;
Map<String, String> AVAILABLEFORMATS;
int FIRSTDAYOFWEEK;
List<int> WEEKENDRANGE;
int FIRSTWEEKCUTOFFDAY;
DateSymbols({this.NAME,
this.ERAS,
this.ERANAMES,
this.NARROWMONTHS,
this.STANDALONENARROWMONTHS,
this.MONTHS,
this.STANDALONEMONTHS,
this.SHORTMONTHS,
this.STANDALONESHORTMONTHS,
this.WEEKDAYS,
this.STANDALONEWEEKDAYS,
this.SHORTWEEKDAYS,
this.STANDALONESHORTWEEKDAYS,
this.NARROWWEEKDAYS,
this.STANDALONENARROWWEEKDAYS,
this.SHORTQUARTERS,
this.QUARTERS,
this.AMPMS,
// TODO(alanknight): These formats are taken from Closure,
// where there's only a fixed set of available formats.
// Here we have the patterns separately. These should
// either be used, or removed.
this.DATEFORMATS,
this.TIMEFORMATS,
this.AVAILABLEFORMATS,
this.FIRSTDAYOFWEEK,
this.WEEKENDRANGE,
this.FIRSTWEEKCUTOFFDAY});
// TODO(alanknight): Replace this with use of a more general serialization
// facility once one is available. Issue 4926.
DateSymbols.deserializeFromMap(Map map) {
NAME = map["NAME"];
ERAS = map["ERAS"];
ERANAMES = map["ERANAMES"];
NARROWMONTHS = map["NARROWMONTHS"];
STANDALONENARROWMONTHS = map["STANDALONENARROWMONTHS"];
MONTHS = map["MONTHS"];
STANDALONEMONTHS = map["STANDALONEMONTHS"];
SHORTMONTHS = map["SHORTMONTHS"];
STANDALONESHORTMONTHS = map["STANDALONESHORTMONTHS"];
WEEKDAYS = map["WEEKDAYS"];
STANDALONEWEEKDAYS = map["STANDALONEWEEKDAYS"];
SHORTWEEKDAYS = map["SHORTWEEKDAYS"];
STANDALONESHORTWEEKDAYS = map["STANDALONESHORTWEEKDAYS"];
NARROWWEEKDAYS = map["NARROWWEEKDAYS"];
STANDALONENARROWWEEKDAYS = map["STANDALONENARROWWEEKDAYS"];
SHORTQUARTERS = map["SHORTQUARTERS"];
QUARTERS = map["QUARTERS"];
AMPMS = map["AMPMS"];
DATEFORMATS = map["DATEFORMATS"];
TIMEFORMATS = map["TIMEFORMATS"];
AVAILABLEFORMATS = map["AVAILABLEFORMATS"];
FIRSTDAYOFWEEK = map["FIRSTDAYOFWEEK"];
WEEKENDRANGE = map["WEEKENDRANGE"];
FIRSTWEEKCUTOFFDAY = map["FIRSTWEEKCUTOFFDAY"];
}
Map serializeToMap() {
var map = new Map();
map["NAME"] = NAME;
map["ERAS"] = ERAS;
map["ERANAMES"] = ERANAMES;
map["NARROWMONTHS"] = NARROWMONTHS;
map["STANDALONENARROWMONTHS"] = STANDALONENARROWMONTHS;
map["MONTHS"] = MONTHS;
map["STANDALONEMONTHS"] = STANDALONEMONTHS;
map["SHORTMONTHS"] = SHORTMONTHS;
map["STANDALONESHORTMONTHS"] = STANDALONESHORTMONTHS;
map["WEEKDAYS"] = WEEKDAYS;
map["STANDALONEWEEKDAYS"] = STANDALONEWEEKDAYS;
map["SHORTWEEKDAYS"] = SHORTWEEKDAYS;
map["STANDALONESHORTWEEKDAYS"] = STANDALONESHORTWEEKDAYS;
map["NARROWWEEKDAYS"] = NARROWWEEKDAYS;
map["STANDALONENARROWWEEKDAYS"] = STANDALONENARROWWEEKDAYS;
map["SHORTQUARTERS"] = SHORTQUARTERS;
map["QUARTERS"] = QUARTERS;
map["AMPMS"] = AMPMS;
map["DATEFORMATS"] = DATEFORMATS;
map["TIMEFORMATS"] = TIMEFORMATS;
map["AVAILABLEFORMATS"] = AVAILABLEFORMATS;
map["FIRSTDAYOFWEEK"] = FIRSTDAYOFWEEK;
map["WEEKENDRANGE"] = WEEKENDRANGE;
map["FIRSTWEEKCUTOFFDAY"] = FIRSTWEEKCUTOFFDAY;
return map;
}
toString() => NAME;
}
/**
* We hard-code the locale data for en_US here so that there's at least one
* locale always available.
*/
var en_USSymbols = new DateSymbols(
NAME: "en_US",
ERAS: const [ 'BC', 'AD'],
ERANAMES: const [ 'Before Christ', 'Anno Domini'],
NARROWMONTHS: const [ 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
'N', 'D'],
STANDALONENARROWMONTHS: const [ 'J', 'F', 'M', 'A', 'M', 'J', 'J', 'A',
'S', 'O', 'N', 'D'],
MONTHS: const [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'],
STANDALONEMONTHS: const [ 'January', 'February', 'March', 'April', 'May',
'June', 'July', 'August', 'September', 'October', 'November',
'December'],
SHORTMONTHS: const [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
STANDALONESHORTMONTHS: const [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
WEEKDAYS: const [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday'],
STANDALONEWEEKDAYS: const [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'],
SHORTWEEKDAYS: const [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
STANDALONESHORTWEEKDAYS: const [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri',
'Sat'],
NARROWWEEKDAYS: const [ 'S', 'M', 'T', 'W', 'T', 'F', 'S'],
STANDALONENARROWWEEKDAYS: const [ 'S', 'M', 'T', 'W', 'T', 'F', 'S'],
SHORTQUARTERS: const [ 'Q1', 'Q2', 'Q3', 'Q4'],
QUARTERS: const [ '1st quarter', '2nd quarter', '3rd quarter',
'4th quarter'],
AMPMS: const [ 'AM', 'PM'],
DATEFORMATS: const [ 'EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y',
'M/d/yy'],
TIMEFORMATS: const [ 'h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a',
'h:mm a'],
FIRSTDAYOFWEEK: 6,
WEEKENDRANGE: const [5, 6],
FIRSTWEEKCUTOFFDAY: 5);
var en_USPatterns = const {
'd': 'd', // DAY
'E': 'EEE', // ABBR_WEEKDAY
'EEEE': 'EEEE', // WEEKDAY
'LLL': 'LLL', // ABBR_STANDALONE_MONTH
'LLLL': 'LLLL', // STANDALONE_MONTH
'M': 'L', // NUM_MONTH
'Md': 'M/d', // NUM_MONTH_DAY
'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
'MMM': 'LLL', // ABBR_MONTH
'MMMd': 'MMM d', // ABBR_MONTH_DAY
'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
'MMMM': 'LLLL', // MONTH
'MMMMd': 'MMMM d', // MONTH_DAY
'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
'QQQ': 'QQQ', // ABBR_QUARTER
'QQQQ': 'QQQQ', // QUARTER
'y': 'y', // YEAR
'yM': 'M/y', // YEAR_NUM_MONTH
'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
'yMMM': 'MMM y', // YEAR_ABBR_MONTH
'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
'yMMMM': 'MMMM y', // YEAR_MONTH
'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
'yQQQQ': 'QQQQ y', // YEAR_QUARTER
'H': 'HH', // HOUR24
'Hm': 'HH:mm', // HOUR24_MINUTE
'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
'j': 'h a', // HOUR
'jm': 'h:mm a', // HOUR_MINUTE
'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
'jmz': 'h:mm a z', // HOUR_MINUTETZ
'jz': 'h a z', // HOURGENERIC_TZ
'm': 'm', // MINUTE
'ms': 'mm:ss', // MINUTE_SECOND
's': 's', // SECOND
'v': 'v', // ABBR_GENERIC_TZ
'z': 'z', // ABBR_SPECIFIC_TZ
'zzzz': 'zzzz', // SPECIFIC_TZ
'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
};
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* Constants for use in metadata annotations such as
* `@deprecated`, `@override`, and `@proxy`.
*
* Annotations provide semantic information
* that tools can use to provide a better user experience.
* For example, an IDE might not autocomplete
* the name of a function that's been marked `@deprecated`,
* or it might display the function's name differently.
*
* For information on installing and importing this library, see the
* [meta package on pub.dartlang.org]
* (http://pub.dartlang.org/packages/meta).
* For examples of using annotations, see
* [Metadata](https://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#ch02-metadata)
* in the language tour.
*/
library meta;
/**
* An annotation used to mark a class, field, getter, setter, method, top-level
* variable, or top-level function as one that should no longer be used. Tools
* can use this annotation to provide a warning on references to the marked
* element.
*/
const deprecated = const _Deprecated();
class _Deprecated {
const _Deprecated();
}
/**
* An annotation used to mark an instance member (method, field, getter or
* setter) as overriding an inherited class member. Tools can use this
* annotation to provide a warning if there is no overridden member.
*/
const override = const _Override();
class _Override {
const _Override();
}
/**
* An annotation used to mark a class that should be considered to implement
* every possible getter, setter and method. Tools can use this annotation to
* suppress warnings when there is no explicit implementation of a referenced
* member. Tools should provide a hint if this annotation is applied to a class
* that does not implement or inherit an implementation of the method
* [:noSuchMethod:] (other than the implementation in [Object]). Note that
* classes are not affected by the use of this annotation on a supertype.
*/
const proxy = const _Proxy();
class _Proxy {
const _Proxy();
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* This library provides internationalization and localization. This includes
* message formatting and replacement, date and number formatting and parsing,
* and utilities for working with Bidirectional text.
*
* ## Installing ##
*
* Use [pub][] to install this package. Add the following to your `pubspec.yaml`
* file.
*
* dependencies:
* intl: any
*
* Then run `pub install`.
*
* For more information, see the
* [intl package on pub.dartlang.org](http://pub.dartlang.org/packages/intl).
*
* For things that require locale or other data, there are multiple different
* ways of making that data available, which may require importing different
* libraries. See the class comments for more details.
*
* There is also a simple example application that can be found in the
* `example/basic` directory.
*
* [pub]: http://pub.dartlang.org
*/
library intl;
import 'dart:collection';
import 'dart:convert';
import 'package:meta/meta.dart';
import 'src/intl_helpers.dart';
import 'dart:math';
import 'date_symbols.dart';
import 'src/date_format_internal.dart';
import "number_symbols.dart";
import "number_symbols_data.dart";
part 'date_format.dart';
part 'src/date_format_field.dart';
part 'src/date_format_helpers.dart';
part 'bidi_formatter.dart';
part 'bidi_utils.dart';
part 'number_format.dart';
/**
* The Intl class provides a common entry point for internationalization
* related tasks. An Intl instance can be created for a particular locale
* and used to create a date format via `anIntl.date()`. Static methods
* on this class are also used in message formatting.
*
* Message example:
* '''I see ${Intl.plural(num_people,
* {'0': 'no one at all',
* '1': 'one other person',
* 'other': '$num_people other people'})} in $place.''''
*
* Usage examples:
* today(date) => Intl.message(
* "Today's date is $date",
* name: 'today',
* args: [date],
* desc: 'Indicate the current date',
* examples: {'date' : 'June 8, 2012'});
* print(today(new DateTime.now());
*
* msg(num_people, place) => Intl.message(
* '''I see ${Intl.plural(num_people,
* {'0': 'no one at all',
* '1': 'one other person',
* 'other': '$num_people other people'})} in $place.''',
* name: 'msg',
* args: [num_people, place],
* desc: 'Description of how many people are seen as program start.',
* examples: {'num_people': 3, 'place': 'London'});
*
* Calling `msg(2, 'Athens');` would
* produce "I see 2 other people in Athens." as output in the default locale.
*
* To use a locale other than the default, use the `withLocale` function.
* You can set the default locale.
* Intl.defaultLocale = "pt_BR";
*
* To temporarily use a locale other than the default, use the `withLocale`
* function.
* var todayString = new DateFormat("pt_BR").format(new DateTime.now());
* print(withLocale("pt_BR", () => today(todayString));
*
* See `tests/message_format_test.dart` for more examples.
*/
//TODO(efortuna): documentation example involving the offset parameter?
class Intl {
/**
* String indicating the locale code with which the message is to be
* formatted (such as en-CA).
*/
String _locale;
/** The default locale. This defaults to being set from systemLocale, but
* can also be set explicitly, and will then apply to any new instances where
* the locale isn't specified.
*/
static String defaultLocale;
/**
* The system's locale, as obtained from the window.navigator.language
* or other operating system mechanism. Note that due to system limitations
* this is not automatically set, and must be set by importing one of
* intl_browser.dart or intl_standalone.dart and calling findSystemLocale().
*/
static String systemLocale = 'en_US';
/**
* Return a new date format using the specified [pattern].
* If [desiredLocale] is not specified, then we default to [locale].
*/
DateFormat date([String pattern, String desiredLocale]) {
var actualLocale = (desiredLocale == null) ? locale : desiredLocale;
return new DateFormat(pattern, actualLocale);
}
/**
* Constructor optionally [aLocale] for specifics of the language
* locale to be used, otherwise, we will attempt to infer it (acceptable if
* Dart is running on the client, we can infer from the browser/client
* preferences).
*/
Intl([String aLocale]) {
if (aLocale != null) {
_locale = aLocale;
} else {
_locale = getCurrentLocale();
}
}
/**
* Returns a message that can be internationalized. It takes a
* [message_str] that will be translated, which may be interpolated
* based on one or more variables, a [desc] providing a description of usage
* for the [message_str], and a map of [examples] for each data element to be
* substituted into the message. For example, if message="Hello, $name", then
* examples = {'name': 'Sparky'}. If not using the user's default locale, or
* if the locale is not easily detectable, explicitly pass [locale].
* The values of [desc] and [examples] are not used at run-time but are only
* made available to the translators, so they MUST be simple Strings available
* at compile time: no String interpolation or concatenation.
* The expected usage of this is inside a function that takes as parameters
* the variables used in the interpolated string, and additionally also a
* locale (optional).
* Ultimately, the information about the enclosing function and its arguments
* will be extracted automatically but for the time being it must be passed
* explicitly in the [name] and [args] arguments.
*/
static String message(String message_str, {final String desc: '',
final Map examples: const {}, String locale, String name,
List<String> args}) {
return messageLookup.lookupMessage(
message_str, desc, examples, locale, name, args);
}
/**
* Return the locale for this instance. If none was set, the locale will
* be the default.
*/
String get locale => _locale;
/**
* Return true if the locale exists, or if it is null. The null case
* is interpreted to mean that we use the default locale.
*/
static bool _localeExists(localeName) {
return DateFormat.localeExists(localeName);
}
/**
* Given [newLocale] return a locale that we have data for that is similar
* to it, if possible.
* If [newLocale] is found directly, return it. If it can't be found, look up
* based on just the language (e.g. 'en_CA' -> 'en'). Also accepts '-'
* as a separator and changes it into '_' for lookup, and changes the
* country to uppercase.
* Note that null is interpreted as meaning the default locale, so if
* [newLocale] is null it will be returned.
*/
static String verifiedLocale(String newLocale, Function localeExists,
{Function onFailure: _throwLocaleError}) {
// TODO(alanknight): Previously we kept a single verified locale on the Intl
// object, but with different verification for different uses, that's more
// difficult. As a result, we call this more often. Consider keeping
// verified locales for each purpose if it turns out to be a performance
// issue.
if (newLocale == null) return getCurrentLocale();
if (localeExists(newLocale)) {
return newLocale;
}
for (var each in
[canonicalizedLocale(newLocale), shortLocale(newLocale)]) {
if (localeExists(each)) {
return each;
}
}
return onFailure(newLocale);
}
/**
* The default action if a locale isn't found in verifiedLocale. Throw
* an exception indicating the locale isn't correct.
*/
static String _throwLocaleError(String localeName) {
throw new ArgumentError("Invalid locale '$localeName'");
}
/** Return the short version of a locale name, e.g. 'en_US' => 'en' */
static String shortLocale(String aLocale) {
if (aLocale.length < 2) return aLocale;
return aLocale.substring(0, 2).toLowerCase();
}
/**
* Return the name [aLocale] turned into xx_YY where it might possibly be
* in the wrong case or with a hyphen instead of an underscore. If
* [aLocale] is null, for example, if you tried to get it from IE,
* return the current system locale.
*/
static String canonicalizedLocale(String aLocale) {
// Locales of length < 5 are presumably two-letter forms, or else malformed.
// Locales of length > 6 are likely to be malformed. In either case we
// return them unmodified and if correct they will be found.
// We treat C as a special case, and assume it wants en_ISO for formatting.
// TODO(alanknight): en_ISO is probably not quite right for the C/Posix
// locale for formatting. Consider adding C to the formats database.
if (aLocale == null) return systemLocale;
if (aLocale == "C") return "en_ISO";
if ((aLocale.length < 5) || (aLocale.length > 6)) return aLocale;
if (aLocale[2] != '-' && (aLocale[2] != '_')) return aLocale;
var lastRegionLetter = aLocale.length == 5 ? "" : aLocale[5].toUpperCase();
return '${aLocale[0]}${aLocale[1]}_${aLocale[3].toUpperCase()}'
'${aLocale[4].toUpperCase()}$lastRegionLetter';
}
/**
* Format a message differently depending on [howMany]. Normally used
* as part of an `Intl.message` text that is to be translated.
* Selects the correct plural form from
* the provided alternatives. The [other] named argument is mandatory.
*/
static String plural(int howMany, {zero, one, two, few, many, other,
desc, examples, locale, name, args}) {
// If we are passed a name and arguments, then we are operating as a
// top-level message, so look up our translation by calling Intl.message
// with ourselves as an argument.
if (name != null) {
return message(
plural(howMany,
zero: zero, one: one, two: two, few: few, many: many, other: other),
name: name,
args: args,
locale: locale);
}
if (other == null) {
throw new ArgumentError("The 'other' named argument must be provided");
}
// TODO(alanknight): This algorithm needs to be locale-dependent.
switch (howMany) {
case 0 : return (zero == null) ? other : zero;
case 1 : return (one == null) ? other : one;
case 2: return (two == null) ? ((few == null) ? other : few) : two;
default:
if (howMany == 3 || howMany == 4 && few != null) return few;
if (howMany > 10 && howMany < 100 && many != null) return many;
return other;
}
throw new ArgumentError("Invalid plural usage for $howMany");
}
/**
* Format a message differently depending on [targetGender]. Normally used as
* part of an Intl.message message that is to be translated.
*/
static String gender(String targetGender,
{String male, String female, String other,
String desc, Map examples, String locale, String name,
List<String>args}) {
// If we are passed a name and arguments, then we are operating as a
// top-level message, so look up our translation by calling Intl.message
// with ourselves as an argument.
if (name != null) {
return message(
gender(targetGender, male: male, female: female, other: other),
name: name,
args: args,
locale: locale);
}
if (other == null) {
throw new ArgumentError("The 'other' named argument must be specified");
}
switch(targetGender) {
case "female" : return female == null ? other : female;
case "male" : return male == null ? other : male;
default: return other;
}
}
/**
* Format a message differently depending on [choice]. We look up the value
* of [choice] in [cases] and return the result, or an empty string if
* it is not found. Normally used as part
* of an Intl.message message that is to be translated.
*/
static String select(String choice, Map<String, String> cases,
{String desc, Map examples, String locale, String name,
List<String>args}) {
// If we are passed a name and arguments, then we are operating as a
// top-level message, so look up our translation by calling Intl.message
// with ourselves as an argument.
if (name != null) {
return message(
select(choice, cases),
name: name,
args: args,
locale: locale);
}
var exact = cases[choice];
if (exact != null) return exact;
var other = cases["other"];
if (other == null)
throw new ArgumentError("The 'other' case must be specified");
return other;
}
/**
* Format the given function with a specific [locale], given a
* [message_function] that takes no parameters. The [message_function] can be
* a simple message function that just returns the result of `Intl.message()`
* it can be a wrapper around a message function that takes arguments, or it
* can be something more complex that manipulates multiple message
* functions.
*
* In either case, the purpose of this is to delay calling [message_function]
* until the proper locale has been set. This returns the result of calling
* [message_function], which could be of an arbitrary type.
*/
static withLocale(String locale, Function message_function) {
// We have to do this silliness because Locale is not known at compile time,
// but must be a static variable in order to be visible to the Intl.message
// invocation.
var oldLocale = getCurrentLocale();
defaultLocale = locale;
var result = message_function();
defaultLocale = oldLocale;
return result;
}
/**
* Accessor for the current locale. This should always == the default locale,
* unless for some reason this gets called inside a message that resets the
* locale.
*/
static String getCurrentLocale() {
if (defaultLocale == null) defaultLocale = systemLocale;
return defaultLocale;
}
toString() => "Intl($locale)";
}
part of angular.core.dom;
/**
* ElementWrapper is an interface for [Block]s and [BlockHole]s. Its purpose is
* to allow treating [Block] and [BlockHole] under same interface so that
* [Block]s can be added after [BlockHole].
*/
abstract class ElementWrapper {
List<dom.Node> elements;
ElementWrapper next;
ElementWrapper previous;
}
/**
* A Block is a fundamental building block of DOM. It is a chunk of DOM which
* Can not be structural changed. It can only have its attributes changed.
* A Block can have [BlockHole]s embedded in its DOM. A [BlockHole] can
* contain other [Block]s and it is the only way in which DOM can be changed
* structurally.
*
* A [Block] is a collection of DOM nodes and [Directive]s for those nodes.
*
* A [Block] is responsible for instantiating the [Directive]s and for
* inserting / removing itself to/from DOM.
*
* A [Block] can be created from [BlockFactory].
*
*/
class Block implements ElementWrapper {
List<dom.Node> elements;
ElementWrapper previous = null;
ElementWrapper next = null;
Function onInsert;
Function onRemove;
Function onMove;
List<dynamic> _directives = [];
Block(List<dom.Node> this.elements);
Block insertAfter(ElementWrapper previousBlock) {
// Update Link List.
next = previousBlock.next;
if (next != null) {
next.previous = this;
}
previous = previousBlock;
previousBlock.next = this;
// Update DOM
List<dom.Node> previousElements = previousBlock.elements;
dom.Node previousElement = previousElements[previousElements.length - 1];
dom.Node insertBeforeElement = previousElement.nextNode;
dom.Node parentElement = previousElement.parentNode;
bool preventDefault = false;
Function insertDomElements = () {
for(var i = 0, ii = elements.length; i < ii; i++) {
parentElement.insertBefore(elements[i], insertBeforeElement);
}
};
if (onInsert != null) {
onInsert({
"preventDefault": () {
preventDefault = true;
return insertDomElements;
},
"element": elements[0]
});
}
if (!preventDefault) {
insertDomElements();
}
return this;
}
Block remove() {
bool preventDefault = false;
Function removeDomElements = () {
for(var j = 0, jj = elements.length; j < jj; j++) {
dom.Node current = elements[j];
dom.Node next = j+1 < jj ? elements[j+1] : null;
while(next != null && current.nextNode != next) {
current.nextNode.remove();
}
elements[j].remove();
}
};
if (onRemove != null) {
onRemove({
"preventDefault": () {
preventDefault = true;
return removeDomElements();
},
"element": elements[0]
});
}
if (!preventDefault) {
removeDomElements();
}
// Remove block from list
if (previous != null && (previous.next = next) != null) {
next.previous = previous;
}
next = previous = null;
return this;
}
Block moveAfter(ElementWrapper previousBlock) {
var previousElements = previousBlock.elements,
previousElement = previousElements[previousElements.length - 1],
insertBeforeElement = previousElement.nextNode,
parentElement = previousElement.parentNode,
blockElements = elements;
for(var i = 0, ii = blockElements.length; i < ii; i++) {
parentElement.insertBefore(blockElements[i], insertBeforeElement);
}
// Remove block from list
previous.next = next;
if (next != null) {
next.previous = previous;
}
// Add block to list
next = previousBlock.next;
if (next != null) {
next.previous = this;
}
previous = previousBlock;
previousBlock.next = this;
return this;
}
}
/**
* A BlockHole is an instance of a hole. BlockHoles designate where child
* [Block]s can be added in parent [Block]. BlockHoles wrap a DOM element,
* and act as references which allows more blocks to be added.
*/
class BlockHole extends ElementWrapper {
List<dom.Node> elements;
ElementWrapper previous;
ElementWrapper next;
BlockHole(List<dom.Node> this.elements);
}
library dart_code_gen;
import '../../core/parser/parser_library.dart'; // For ParserBackend.
import 'source.dart';
Code VALUE_CODE = new Code("value");
class Code implements ParserAST {
String id;
String _exp;
String simpleGetter;
Function assign;
Code(this._exp, [this.assign, this.simpleGetter]) {
id = _exp == null ? simpleGetter : _exp;
if (id == null) {
throw 'id is null';
}
}
get exp {
if (_exp == null) {
throw "Can not be used in an expression";
}
return _exp;
}
get assignable => assign != null;
Source toSource(SourceBuilder _) {
return _('new Expression', _.parens(
_('(scope, [locals])', _.body(
'return $exp;'
)),
assignable ? _('(scope, value, [locals])', _.body(
'return ${assign(VALUE_CODE).exp};'
)) : 'null'
));
}
}
class ThrowCode extends Code {
ThrowCode(code): super('throw $code');
Source toSource(SourceBuilder _) {
return _('new Expression', _.parens(
_('(scope, [locals])', _.body()..source.addAll(exp.split('\n'))),
assignable ? _('(scope, value, [locals])', _.body()) : 'null'
));
}
}
class MultipleStatementCode extends Code {
MultipleStatementCode(code): super(code);
Source toSource(SourceBuilder _) {
return _('new Expression', _.parens(
_('(scope, [locals])', _.body()..source.addAll(exp.split('\n'))),
assignable ? _('(scope, value, [locals])', _.body()) : 'null'
));
}
}
class FilterCode extends Code {
final String filterName;
final Code leftHandSide;
final List<Expression> parameters;
final Function evalError;
FilterCode(String this.filterName,
Code this.leftHandSide,
List<Code> this.parameters,
Function this.evalError): super(null);
get id => '${leftHandSide.id} | $filterName:${parameters.map((e)=>e.id).join(':')}}';
Source toSource(SourceBuilder _) {
var params = parameters.map((e) => _.ref(e));
return _('new FilterExpression', _.parens(
'filters(${_.str(filterName)})',
_.ref(leftHandSide),
'[${params.join(', ')}]'
));
}
}
escape(String s) => s.replaceAll('\'', '\\\'').replaceAll(r'$', r'\$');
class GetterSetterGenerator {
static RegExp LAST_PATH_PART = new RegExp(r'(.*)\.(.*)');
static RegExp NON_WORDS = new RegExp(r'\W');
// From https://www.dartlang.org/docs/spec/latest/dart-language-specification.html#h.huusvrzea3q
static List<String> RESERVED_DART_KEYWORDS = [
"assert", "break", "case", "catch", "class", "const", "continue",
"default", "do", "else", "enum", "extends", "false", "final",
"finally", "for", "if", "in", "is", "new", "null", "rethrow",
"return", "super", "switch", "this", "throw", "true", "try",
"var", "void", "while", "with"];
isReserved(String key) => RESERVED_DART_KEYWORDS.contains(key);
String functions = "// GETTER AND SETTER FUNCTIONS\n\n";
var _keyToGetterFnName = {};
var _keyToSetterFnName = {};
var nextUid = 0;
_flatten(key) => key.replaceAll(NON_WORDS, '_');
fieldGetter(String field, String obj) {
var eKey = escape(field);
var returnValue = isReserved(field) ? "undefined_ /* $field is reserved */" : "$obj.$field";
return """
if ($obj is Map) {
if ($obj.containsKey('$eKey')) {
val = $obj['$eKey'];
} else {
val = undefined_;
}
} else {
val = $returnValue;
}
""";
}
fieldSetter(String field, String obj) {
var eKey = escape(field);
var maybeField = isReserved(field) ? "/* $field is reserved */" : """
$obj.$field = value;
return value;
""";
return """
if ($obj is Map) {
$obj['$eKey'] = value;
return value;
}
$maybeField
}
""";
}
call(String key) {
if (_keyToGetterFnName.containsKey(key)) {
return _keyToGetterFnName[key];
}
var fnName = "_${_flatten(key)}";
var keys = key.split('.');
var lines = [
"$fnName(s, [l]) { // for $key"];
_(line) => lines.add(' $line');
for(var i = 0; i < keys.length; i++) {
var k = keys[i];
var sk = isReserved(k) ? "null" : "s.$k";
if (i == 0) {
_('if (l != null && l.containsKey("${escape(k)}")) s = l["${escape(k)}"];');
_('else if (s != null ) s = s is Map ? s["${escape(k)}"] : $sk;');
} else {
_('if (s != null ) s = s is Map ? s["${escape(k)}"] : $sk;');
}
}
_('return s;');
lines.add('}\n\n');
functions += lines.join('\n');
_keyToGetterFnName[key] = fnName;
return fnName;
}
setter(String key) {
if (_keyToSetterFnName.containsKey(key)) {
return _keyToSetterFnName[key];
}
var fnName = "_set_${_flatten(key)}";
var lines = [
"$fnName(s, v, [l]) { // for $key"];
_(line) => lines.add(' $line');
var keys = key.split('.');
_(keys.length == 1 ? 'var n = s;' : 'var n;');
var k = keys[0];
var sk = isReserved(k) ? "null" : "s.$k";
var nk = isReserved(k) ? "null" : "n.$k";
if (keys.length > 1) {
// locals
_('if (l != null) n = l["${escape(k)}"];');
_('if (l == null || (n == null && !l.containsKey("${escape(k)}"))) n = s is Map ? s["${escape(k)}"] : $sk;');
_('if (n == null) n = s is Map ? (s["${escape(k)}"] = {}) : ($sk = {});');
}
for(var i = 1; i < keys.length - 1; i++) {
k = keys[i];
sk = isReserved(k) ? "null" : "s.$k";
nk = isReserved(k) ? "null" : "n.$k";
// middle
_('s = n; n = n is Map ? n["${escape(k)}"] : $nk;');
_('if (n == null) n = s is Map ? (s["${escape(k)}"] = {}) : (${isReserved(k) ? "null" : "$sk = {}"});');
}
k = keys[keys.length - 1];
sk = isReserved(k) ? "null" : "s.$k";
nk = isReserved(k) ? "null" : "n.$k";
_('if (n is Map) n["${escape(k)}"] = v; else ${isReserved(k) ? "null" : "$nk = v"};');
// finish
_('return v;');
lines.add('}\n\n');
functions += lines.join('\n');
_keyToSetterFnName[key] = fnName;
return fnName;
}
}
class DartCodeGen implements ParserBackend {
static Code ZERO = new Code("0");
GetterSetterGenerator _getterGen;
DartCodeGen(GetterSetterGenerator this._getterGen);
// Returns the Dart code for a particular operator.
_op(fn) => fn == "undefined" ? "null" : fn;
Code binaryFn(Code left, String fn, Code right) {
if (fn == '+') {
return new Code("autoConvertAdd(${left.exp}, ${right.exp})");
}
var leftExp = left.exp;
var rightExp = right.exp;
if (fn == '&&' || fn == '||') {
leftExp = "toBool($leftExp)";
rightExp = "toBool($rightExp)";
}
return new Code("(${leftExp} ${_op(fn)} ${rightExp})");
}
Code unaryFn(String fn, Code right) {
var rightExp = right.exp;
if (fn == '!') {
rightExp = "toBool($rightExp)";
}
return new Code("${_op(fn)}${rightExp}");
}
Code assignment(Code left, Code right, evalError) =>
left.assign(right);
Code multipleStatements(List<Code >statements) {
var code = "var ret, last;\n";
code += statements.map((Code s) =>
"last = ${s.exp};\nif (last != null) { ret = last; }\n").join('\n');
code += "return ret;\n";
return new MultipleStatementCode(code);
}
Code functionCall(Code fn, fnName, List<Code> argsFn, evalError) =>
new Code("safeFunctionCall(${fn.exp}, \'${escape(fnName)}\', evalError)(${argsFn.map((a) => a.exp).join(', ')})");
Code arrayDeclaration(List<Code> elementFns) =>
new Code("[${elementFns.map((Code e) => e.exp).join(', ')}]");
Code objectIndex(Code obj, Code indexFn, evalError) {
var assign = (Code right) =>
new Code("objectIndexSetField(${obj.exp}, ${indexFn.exp}, ${right.exp}, evalError)");
return new Code("objectIndexGetField(${obj.exp}, ${indexFn.exp}, evalError)", assign);
}
Code fieldAccess(Code object, String field) {
var getterFnName = _getterGen(field);
var assign = (Code right) {
var setterFnName = _getterGen.setter(field);
return new Code("$setterFnName(${object.exp}, ${right.exp})");
};
return new Code("$getterFnName/*field:$field*/(${object.exp}, null)", assign);
}
Code object(List keyValues) =>
new Code(
"{${keyValues.map((k) => "${_value(k["key"])}: ${k["value"].exp}").join(', ')}}");
profiled(value, perf, text) => value; // no profiling for now
Code fromOperator(String op) => new Code(_op(op));
Code getterSetter(String key) {
var getterFnName = _getterGen(key);
var assign = (Code right) {
var setterFnName = _getterGen.setter(key);
return new Code("${setterFnName}(scope, ${right.exp}, locals)", null, setterFnName);
};
return new Code("$getterFnName(scope, locals)", assign, getterFnName);
}
String _value(v) =>
v is String ? "r\'${escape(v)}\'" : "$v";
Code value(v) => new Code(_value(v));
Code zero() => ZERO;
Code filter(String filterName,
Code leftHandSide,
List<Code> parameters,
Function evalError) {
return new FilterCode(filterName, leftHandSide, parameters, evalError);
}
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* Date/time formatting symbols for all locales.
*
* DO NOT EDIT. This file is autogenerated by script. See
* http://go/generate_number_constants.py using the --for_dart flag.
*
* Before checkin, this file could have been manually edited. This is
* to incorporate changes before we could correct CLDR. All manual
* modification must be documented in this section, and should be
* removed after those changes land to CLDR.
*/
library number_symbol_data;
import "number_symbols.dart";
Map numberFormatSymbols = const {
/**
* Number formatting symbols for locale af.
*/
"af" : const NumberSymbols(
NAME: "af",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'ZAR'),
/**
* Number formatting symbols for locale am.
*/
"am" : const NumberSymbols(
NAME: "am",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'ETB'),
/**
* Number formatting symbols for locale ar.
*/
"ar" : const NumberSymbols(
NAME: "ar",
DECIMAL_SEP: '\u066B',
GROUP_SEP: '\u066C',
PERCENT: '\u066A',
ZERO_DIGIT: '\u0660',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: '\u0627\u0633',
PERMILL: '\u0609',
INFINITY: '\u221E',
NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
DECIMAL_PATTERN: '#0.###;#0.###-',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#0.00;\u00A4\u00A0#0.00-',
DEF_CURRENCY_CODE: 'EGP'),
/**
* Number formatting symbols for locale bg.
*/
"bg" : const NumberSymbols(
NAME: "bg",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'BGN'),
/**
* Number formatting symbols for locale bn.
*/
"bn" : const NumberSymbols(
NAME: "bn",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '\u09e6',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u09B8\u0982\u0996\u09CD\u09AF\u09BE\u00A0\u09A8\u09BE',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '#,##,##0.00\u00A4;(#,##,##0.00\u00A4)',
DEF_CURRENCY_CODE: 'BDT'),
/**
* Number formatting symbols for locale ca.
*/
"ca" : const NumberSymbols(
NAME: "ca",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale cs.
*/
"cs" : const NumberSymbols(
NAME: "cs",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'CZK'),
/**
* Number formatting symbols for locale da.
*/
"da" : const NumberSymbols(
NAME: "da",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'DKK'),
/**
* Number formatting symbols for locale de.
*/
"de" : const NumberSymbols(
NAME: "de",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale de_AT.
*/
"de_AT" : const NumberSymbols(
NAME: "de_AT",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale de_CH.
*/
"de_CH" : const NumberSymbols(
NAME: "de_CH",
DECIMAL_SEP: '.',
GROUP_SEP: '\'',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4-#,##0.00',
DEF_CURRENCY_CODE: 'CHF'),
/**
* Number formatting symbols for locale el.
*/
"el" : const NumberSymbols(
NAME: "el",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'e',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '[#E0]',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale en.
*/
"en" : const NumberSymbols(
NAME: "en",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'USD'),
/**
* Number formatting symbols for locale en_AU.
*/
"en_AU" : const NumberSymbols(
NAME: "en_AU",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'AUD'),
/**
* Number formatting symbols for locale en_GB.
*/
"en_GB" : const NumberSymbols(
NAME: "en_GB",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'GBP'),
/**
* Number formatting symbols for locale en_IE.
*/
"en_IE" : const NumberSymbols(
NAME: "en_IE",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale en_IN.
*/
"en_IN" : const NumberSymbols(
NAME: "en_IN",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale en_SG.
*/
"en_SG" : const NumberSymbols(
NAME: "en_SG",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'SGD'),
/**
* Number formatting symbols for locale en_US.
*/
"en_US" : const NumberSymbols(
NAME: "en_US",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'USD'),
/**
* Number formatting symbols for locale en_ZA.
*/
"en_ZA" : const NumberSymbols(
NAME: "en_ZA",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'ZAR'),
/**
* Number formatting symbols for locale es.
*/
"es" : const NumberSymbols(
NAME: "es",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale es_419.
*/
"es_419" : const NumberSymbols(
NAME: "es_419",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'MXN'),
/**
* Number formatting symbols for locale et.
*/
"et" : const NumberSymbols(
NAME: "et",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#0.00\u00A4;(#0.00\u00A4)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale eu.
*/
"eu" : const NumberSymbols(
NAME: "eu",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '%\u00A0#,##0',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4;(#,##0.00\u00A0\u00A4)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale fa.
*/
"fa" : const NumberSymbols(
NAME: "fa",
DECIMAL_SEP: '\u066B',
GROUP_SEP: '\u066C',
PERCENT: '\u066A',
ZERO_DIGIT: '\u06F0',
PLUS_SIGN: '+',
MINUS_SIGN: '\u2212',
EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
PERMILL: '\u0609',
INFINITY: '\u221E',
NAN: '\u0646\u0627\u0639\u062F\u062F',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u200E\u00A4#,##0.00;\u200E(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'IRR'),
/**
* Number formatting symbols for locale fi.
*/
"fi" : const NumberSymbols(
NAME: "fi",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'ep\u00E4luku',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale fil.
*/
"fil" : const NumberSymbols(
NAME: "fil",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'PHP'),
/**
* Number formatting symbols for locale fr.
*/
"fr" : const NumberSymbols(
NAME: "fr",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4;(#,##0.00\u00A0\u00A4)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale fr_CA.
*/
"fr_CA" : const NumberSymbols(
NAME: "fr_CA",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4;(#,##0.00\u00A0\u00A4)',
DEF_CURRENCY_CODE: 'CAD'),
/**
* Number formatting symbols for locale gl.
*/
"gl" : const NumberSymbols(
NAME: "gl",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale gsw.
*/
"gsw" : const NumberSymbols(
NAME: "gsw",
DECIMAL_SEP: '.',
GROUP_SEP: '\u2019',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '\u2212',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'CHF'),
/**
* Number formatting symbols for locale gu.
*/
"gu" : const NumberSymbols(
NAME: "gu",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale he.
*/
"he" : const NumberSymbols(
NAME: "he",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'ILS'),
/**
* Number formatting symbols for locale hi.
*/
"hi" : const NumberSymbols(
NAME: "hi",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale hr.
*/
"hr" : const NumberSymbols(
NAME: "hr",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'HRK'),
/**
* Number formatting symbols for locale hu.
*/
"hu" : const NumberSymbols(
NAME: "hu",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'HUF'),
/**
* Number formatting symbols for locale id.
*/
"id" : const NumberSymbols(
NAME: "id",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'IDR'),
/**
* Number formatting symbols for locale in.
*/
"in" : const NumberSymbols(
NAME: "in",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'IDR'),
/**
* Number formatting symbols for locale is.
*/
"is" : const NumberSymbols(
NAME: "is",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'ISK'),
/**
* Number formatting symbols for locale it.
*/
"it" : const NumberSymbols(
NAME: "it",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale iw.
*/
"iw" : const NumberSymbols(
NAME: "iw",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'ILS'),
/**
* Number formatting symbols for locale ja.
*/
"ja" : const NumberSymbols(
NAME: "ja",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'JPY'),
/**
* Number formatting symbols for locale kn.
*/
"kn" : const NumberSymbols(
NAME: "kn",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: '\u0C88',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale ko.
*/
"ko" : const NumberSymbols(
NAME: "ko",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'KRW'),
/**
* Number formatting symbols for locale ln.
*/
"ln" : const NumberSymbols(
NAME: "ln",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'CDF'),
/**
* Number formatting symbols for locale lt.
*/
"lt" : const NumberSymbols(
NAME: "lt",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '\u2013',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'LTL'),
/**
* Number formatting symbols for locale lv.
*/
"lv" : const NumberSymbols(
NAME: "lv",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'nav\u00A0skaitlis',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'LVL'),
/**
* Number formatting symbols for locale ml.
*/
"ml" : const NumberSymbols(
NAME: "ml",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '#,##,##0.00\u00A4',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale mr.
*/
"mr" : const NumberSymbols(
NAME: "mr",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale ms.
*/
"ms" : const NumberSymbols(
NAME: "ms",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'MYR'),
/**
* Number formatting symbols for locale mt.
*/
"mt" : const NumberSymbols(
NAME: "mt",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale nl.
*/
"nl" : const NumberSymbols(
NAME: "nl",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale no.
*/
"no" : const NumberSymbols(
NAME: "no",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
DEF_CURRENCY_CODE: 'NOK'),
/**
* Number formatting symbols for locale or.
*/
"or" : const NumberSymbols(
NAME: "or",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale pl.
*/
"pl" : const NumberSymbols(
NAME: "pl",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4;(#,##0.00\u00A0\u00A4)',
DEF_CURRENCY_CODE: 'PLN'),
/**
* Number formatting symbols for locale pt.
*/
"pt" : const NumberSymbols(
NAME: "pt",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'BRL'),
/**
* Number formatting symbols for locale pt_BR.
*/
"pt_BR" : const NumberSymbols(
NAME: "pt_BR",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'BRL'),
/**
* Number formatting symbols for locale pt_PT.
*/
"pt_PT" : const NumberSymbols(
NAME: "pt_PT",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale ro.
*/
"ro" : const NumberSymbols(
NAME: "ro",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'RON'),
/**
* Number formatting symbols for locale ru.
*/
"ru" : const NumberSymbols(
NAME: "ru",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'RUB'),
/**
* Number formatting symbols for locale sk.
*/
"sk" : const NumberSymbols(
NAME: "sk",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale sl.
*/
"sl" : const NumberSymbols(
NAME: "sl",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'e',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'EUR'),
/**
* Number formatting symbols for locale sq.
*/
"sq" : const NumberSymbols(
NAME: "sq",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'ALL'),
/**
* Number formatting symbols for locale sr.
*/
"sr" : const NumberSymbols(
NAME: "sr",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'RSD'),
/**
* Number formatting symbols for locale sv.
*/
"sv" : const NumberSymbols(
NAME: "sv",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '\u2212',
EXP_SYMBOL: '\u00D710^',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u00A4\u00A4\u00A4',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'SEK'),
/**
* Number formatting symbols for locale sw.
*/
"sw" : const NumberSymbols(
NAME: "sw",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'TZS'),
/**
* Number formatting symbols for locale ta.
*/
"ta" : const NumberSymbols(
NAME: "ta",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale te.
*/
"te" : const NumberSymbols(
NAME: "te",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'INR'),
/**
* Number formatting symbols for locale th.
*/
"th" : const NumberSymbols(
NAME: "th",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'THB'),
/**
* Number formatting symbols for locale tl.
*/
"tl" : const NumberSymbols(
NAME: "tl",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'PHP'),
/**
* Number formatting symbols for locale tr.
*/
"tr" : const NumberSymbols(
NAME: "tr",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '%#,##0',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4;(#,##0.00\u00A0\u00A4)',
DEF_CURRENCY_CODE: 'TRY'),
/**
* Number formatting symbols for locale uk.
*/
"uk" : const NumberSymbols(
NAME: "uk",
DECIMAL_SEP: ',',
GROUP_SEP: '\u00A0',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: '\u0415',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u041D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'UAH'),
/**
* Number formatting symbols for locale ur.
*/
"ur" : const NumberSymbols(
NAME: "ur",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'PKR'),
/**
* Number formatting symbols for locale vi.
*/
"vi" : const NumberSymbols(
NAME: "vi",
DECIMAL_SEP: ',',
GROUP_SEP: '.',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'VND'),
/**
* Number formatting symbols for locale zh.
*/
"zh" : const NumberSymbols(
NAME: "zh",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'CNY'),
/**
* Number formatting symbols for locale zh_CN.
*/
"zh_CN" : const NumberSymbols(
NAME: "zh_CN",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'CNY'),
/**
* Number formatting symbols for locale zh_HK.
*/
"zh_HK" : const NumberSymbols(
NAME: "zh_HK",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u975E\u6578\u503C',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'HKD'),
/**
* Number formatting symbols for locale zh_TW.
*/
"zh_TW" : const NumberSymbols(
NAME: "zh_TW",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: '\u975E\u6578\u503C',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'TWD'),
/**
* Number formatting symbols for locale zu.
*/
"zu" : const NumberSymbols(
NAME: "zu",
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PERCENT: '%',
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'I-NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00;(\u00A4#,##0.00)',
DEF_CURRENCY_CODE: 'ZAR')
};
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library number_symbols;
/**
* This holds onto information about how a particular locale formats numbers. It
* contains strings for things like the decimal separator, digit to use for "0"
* and infinity. We expect the data for instances to be generated out of ICU
* or a similar reference source.
*/
class NumberSymbols {
final String NAME;
final String DECIMAL_SEP, GROUP_SEP, PERCENT, ZERO_DIGIT, PLUS_SIGN,
MINUS_SIGN, EXP_SYMBOL, PERMILL, INFINITY, NAN, DECIMAL_PATTERN,
SCIENTIFIC_PATTERN, PERCENT_PATTERN, CURRENCY_PATTERN, DEF_CURRENCY_CODE;
const NumberSymbols({this.NAME,
this.DECIMAL_SEP,
this.GROUP_SEP,
this.PERCENT,
this.ZERO_DIGIT,
this.PLUS_SIGN,
this.MINUS_SIGN,
this.EXP_SYMBOL,
this.PERMILL,
this.INFINITY,
this.NAN,
this.DECIMAL_PATTERN,
this.SCIENTIFIC_PATTERN,
this.PERCENT_PATTERN,
this.CURRENCY_PATTERN,
this.DEF_CURRENCY_CODE});
toString() => NAME;
}
part of angular.directive;
/**
* The `ngCloak` directive is used to prevent the Angular html template from
* being briefly displayed by the browser in its raw (uncompiled) form while
* your application is loading. Use this directive to avoid the undesirable
* flicker effect caused by the html template display.
*
* The directive can be applied to the `<body>` element, but typically a
* fine-grained application is preferred in order to benefit from progressive
* rendering of the browser view.
*
* `ngCloak` works in cooperation with a css. Following is the css rule:
*
* [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
* display: none !important;
* }
*
* When this css rule is loaded by the browser, all html elements (including
* their children) that are tagged with the `ng-cloak` directive are hidden.
* When Angular comes across this directive during the compilation of the
* template it deletes the `ngCloak` element attribute, which makes the compiled
* element visible.
*/
@NgDirective(selector: '[ng-cloak]')
@NgDirective(selector: '.ng-cloak')
class NgCloakDirective {
NgCloakDirective(dom.Element element) {
element.attributes.remove('ng-cloak');
element.classes.remove('ng-cloak');
}
}
library di.dynamic_injector;
import 'mirrors.dart';
import 'errors.dart';
import 'module.dart';
import 'injector.dart';
/**
* Dynamic implementation of [Injector] that uses mirrors.
*/
class DynamicInjector implements Injector {
final bool allowImplicitInjection;
final String name;
final DynamicInjector parent;
final Map<Symbol, _ProviderMetadata> providers =
new Map<Symbol, _ProviderMetadata>();
final Map<Symbol, Object> instances = new Map<Symbol, Object>();
final List<Symbol> resolving = new List<Symbol>();
final List<Type> _types = [];
DynamicInjector({List<Module> modules, String name,
bool allowImplicitInjection: false})
: this._fromParent(modules, null, name: name,
allowImplicitInjection: allowImplicitInjection);
DynamicInjector._fromParent(List<Module> modules, Injector this.parent,
{bool this.allowImplicitInjection: false, this.name}) {
if (modules == null) {
modules = <Module>[];
}
modules.forEach((module) {
module.bindings.forEach(_registerBinding);
});
_registerBinding(Injector, new ValueBinding(this));
}
_registerBinding(Type type, Binding binding) {
this._types.add(type);
var symbol = getTypeSymbol(type);
if (binding is ValueBinding) {
providers[symbol] = new _ProviderMetadata.forValue(binding);
} else if (binding is TypeBinding) {
providers[symbol] = new _ProviderMetadata.forType(binding);
} else if (binding is FactoryBinding) {
providers[symbol] = new _ProviderMetadata.forFactory(binding);
} else {
throw 'Unknown binding type ${binding.runtimeType}';
}
}
Set<Type> get types {
var types = new Set.from(_types);
var parent = this.parent;
while (parent != null) {
for(var type in parent._types) {
if (!types.contains(type)) {
types.add(type);
}
}
parent = parent.parent;
}
return types;
}
String _error(message, [appendDependency]) {
if (appendDependency != null) {
resolving.add(appendDependency);
}
String graph = resolving.map(getSymbolSimpleName).join(' -> ');
resolving.clear();
return '$message (resolving $graph)';
}
dynamic _getInstanceBySymbol(Symbol typeName, Injector requester) {
_checkTypeConditions(typeName);
if (resolving.contains(typeName)) {
throw new CircularDependencyError(
_error('Cannot resolve a circular dependency!', typeName));
}
var providerWithInjector = _getProviderForSymbol(typeName);
var metadata = providerWithInjector.providerMetadata;
var visible =
metadata.binding.visibility(requester, providerWithInjector.injector);
if (visible && instances.containsKey(typeName)) {
return instances[typeName];
}
if (providerWithInjector.injector != this || !visible) {
var injector = providerWithInjector.injector;
if (!visible) {
injector = providerWithInjector.injector.parent.
_getProviderForSymbol(typeName).injector;
}
return injector._getInstanceBySymbol(typeName, requester);
}
var getInstanceBySymbol =
_wrapGetInstanceBySymbol(_getInstanceBySymbol, requester);
var value;
try {
value = metadata.binding.creationStrategy(requester,
providerWithInjector.injector, () {
resolving.add(typeName);
var val = metadata.provider.get(getInstanceBySymbol, _error);
resolving.removeLast();
return val;
});
} catch(e) {
resolving.clear();
rethrow;
}
// cache the value.
providerWithInjector.injector.instances[typeName] = value;
return value;
}
/**
* Wraps getInstanceBySymbol function with a requster value to be easily
* down to the providers.
*/
ObjectFactory _wrapGetInstanceBySymbol(Function getInstanceBySymbol,
Injector requester) {
return (Symbol typeName) {
return getInstanceBySymbol(typeName, requester);
};
}
/// Returns a pair for provider and the injector where it's defined.
_ProviderWithDefiningInjector _getProviderForSymbol(Symbol typeName) {
if (providers.containsKey(typeName)) {
return new _ProviderWithDefiningInjector(providers[typeName], this);
}
if (parent != null) {
return parent._getProviderForSymbol(typeName);
}
if (!allowImplicitInjection) {
throw new NoProviderError(_error('No provider found for '
'${getSymbolSimpleName(typeName)}!', typeName));
}
// create a provider for implicit types
return new _ProviderWithDefiningInjector(
new _ProviderMetadata.forSymbol(typeName), this);
}
void _checkTypeConditions(Symbol typeName) {
if (PRIMITIVE_TYPES.contains(typeName)) {
throw new NoProviderError(_error('Cannot inject a primitive type '
'of ${getSymbolSimpleName(typeName)}!', typeName));
}
}
// PUBLIC API
/**
* Get an instance for given token ([Type]).
*
* If the injector already has an instance for this token, it returns this
* instance. Otherwise, injector resolves all its dependencies, instantiate
* new instance and returns this instance.
*
* If there is no binding for given token, injector asks parent injector.
*
* If there is no parent injector, an implicit binding is used. That is,
* the token ([Type]) is instantiated.
*/
dynamic get(Type type) =>
_getInstanceBySymbol(getTypeSymbol(type), this);
/**
* Invoke given function and inject all its arguments.
*
* Returns whatever the function returns.
*/
dynamic invoke(Function fn) {
ClosureMirror cm = reflect(fn);
MethodMirror mm = cm.function;
num position = 0;
List args = mm.parameters.map((ParameterMirror parameter) {
try {
return _getInstanceBySymbol(parameter.type.qualifiedName, this);
} on NoProviderError catch (e) {
throw new NoProviderError(e.message + ' at position $position source:\n ${mm.source}.');
} finally {
position++;
}
}).toList();
try {
return cm.apply(args, null).reflectee;
} catch (e) {
if (e is MirroredUncaughtExceptionError) {
throw "${e}\nORIGINAL STACKTRACE\n${(e as MirroredUncaughtExceptionError).stacktrace}";
}
rethrow;
}
}
/**
* Create a child injector.
*
* Child injector can override any bindings by adding additional modules.
*
* It also accepts a list of tokens that a new instance should be forced.
* That means, even if some parent injector already has an instance for this
* token, there will be a new instance created in the child injector.
*/
Injector createChild(List<Module> modules,
{List<Type> forceNewInstances, String name}) {
if (forceNewInstances != null) {
Module forceNew = new Module();
forceNewInstances.forEach((Type type) {
var providerWithInjector = _getProviderForSymbol(getTypeSymbol(type));
var metadata = providerWithInjector.providerMetadata;
forceNew.factory(type,
(DynamicInjector inj) => metadata.provider.get(
_wrapGetInstanceBySymbol(inj._getInstanceBySymbol, inj),
inj._error),
creation: metadata.binding.creationStrategy,
visibility: metadata.binding.visibility);
});
modules = modules.toList(); // clone
modules.add(forceNew);
}
return new DynamicInjector._fromParent(modules, this, name: name);
}
}
class _ProviderWithDefiningInjector {
final _ProviderMetadata providerMetadata;
final DynamicInjector injector;
_ProviderWithDefiningInjector(this.providerMetadata, this.injector);
}
typedef Object ObjectFactory(Symbol symbol);
abstract class _Provider {
dynamic get(ObjectFactory getInstanceBySymbol, error);
}
class _ValueProvider implements _Provider {
dynamic value;
_ValueProvider(value) {
this.value = value;
}
dynamic get(getInstanceBySymbol, error) {
return value;
}
}
class _TypeProvider implements _Provider {
final ClassMirror classMirror;
final Symbol typeName;
_TypeProvider(Symbol typeName)
: this.typeName = typeName,
this.classMirror = getClassMirrorBySymbol(typeName);
dynamic get(getInstanceBySymbol, error) {
if (classMirror is TypedefMirror) {
throw new NoProviderError(error('No implementation provided '
'for ${getSymbolName(classMirror.qualifiedName)} typedef!'));
}
MethodMirror ctor = classMirror.constructors[classMirror.simpleName];
resolveArgument(int pos) {
ParameterMirror p = ctor.parameters[pos];
try {
return getInstanceBySymbol(p.type.qualifiedName);
} on NoProviderError catch (e) {
throw new NoProviderError(e.message + ' at position $pos source:\n ${ctor.source}.');
}
}
var args = new List.generate(ctor.parameters.length, resolveArgument,
growable: false);
return classMirror.newInstance(ctor.constructorName, args).reflectee;
}
}
class _FactoryProvider implements _Provider {
final Function factoryFn;
_FactoryProvider(Function this.factoryFn);
dynamic get(getInstanceBySymbol, error) {
return Function.apply(factoryFn,
[getInstanceBySymbol(getTypeSymbol(Injector))]);
}
}
class _ProviderMetadata {
_Provider provider;
Binding binding;
_ProviderMetadata.forValue(ValueBinding binding) {
provider = new _ValueProvider(binding.value);
this.binding = binding;
}
_ProviderMetadata.forType(TypeBinding binding) {
provider = new _TypeProvider(getTypeSymbol(binding.type));
this.binding = binding;
}
_ProviderMetadata.forSymbol(Symbol symbol) {
provider = new _TypeProvider(symbol);
this.binding = new TypeBinding(null);
}
_ProviderMetadata.forFactory(FactoryBinding binding) {
provider = new _FactoryProvider(binding.factoryFn);
this.binding = binding;
}
}
part of angular.directive;
@NgDirective(
selector: '[ng-include]',
map: const {'ng-include': '=.url'} )
class NgIncludeDirective {
dom.Element element;
Scope scope;
BlockCache blockCache;
Injector injector;
Block _previousBlock;
Scope _previousScope;
NgIncludeDirective(dom.Element this.element,
Scope this.scope,
BlockCache this.blockCache,
Injector this.injector);
_cleanUp() {
if (_previousBlock == null) {
return;
}
_previousBlock.remove();
_previousScope.$destroy();
element.innerHtml = '';
_previousBlock = null;
_previousScope = null;
}
_updateContent(createBlock) {
_cleanUp();
// create a new scope
_previousScope = scope.$new();
_previousBlock = createBlock(injector.createChild([new Module()..value(Scope, _previousScope)]));
_previousBlock.elements.forEach((elm) => element.append(elm));
}
set url(value) {
if (value == null || value == '') {
_cleanUp();
return;
}
if (value.startsWith('<')) {
// inlined template
_updateContent(blockCache.fromHtml(value));
} else {
// an url template
blockCache.fromUrl(value).then((createBlock) => _updateContent(createBlock));
}
}
}
library angular.directive;
import 'package:di/di.dart';
import 'dart:html' as dom;
import 'dart:async' as async;
import '../core/module.dart';
import '../core/parser/parser_library.dart';
import '../core_dom/module.dart';
import '../utils.dart';
part 'ng_a.dart';
part 'ng_bind.dart';
part 'ng_bind_html.dart';
part 'ng_bind_template.dart';
part 'ng_class.dart';
part 'ng_events.dart';
part 'ng_cloak.dart';
part 'ng_if.dart';
part 'ng_include.dart';
part 'ng_model.dart';
part 'ng_repeat.dart';
part 'ng_template.dart';
part 'ng_show_hide.dart';
part 'ng_src_boolean.dart';
part 'ng_style.dart';
part 'ng_switch.dart';
part 'ng_non_bindable.dart';
part 'input_select.dart';
class NgDirectiveModule extends Module {
NgDirectiveModule() {
value(NgADirective, null);
value(NgBindDirective, null);
value(NgBindTemplateDirective, null);
value(NgBindHtmlDirective, null);
value(NgClassDirective, null);
value(NgClassOddDirective, null);
value(NgClassEvenDirective, null);
value(NgCloakDirective, null);
value(NgHideDirective, null);
value(NgIfDirective, null);
value(NgUnlessDirective, null);
value(NgIncludeDirective, null);
value(NgRepeatDirective, null);
value(NgShowDirective, null);
value(InputTextDirective, null);
value(InputCheckboxDirective, null);
value(InputSelectDirective, null);
value(OptionValueDirective, null);
value(NgModel, null);
value(NgSwitchDirective, null);
value(NgSwitchWhenDirective, null);
value(NgSwitchDefaultDirective, null);
value(NgBooleanAttributeDirective, null);
value(NgSourceDirective, null);
value(NgEventDirective, null);
value(NgStyleDirective, null);
value(NgNonBindableDirective, null);
value(NgTemplateDirective, null);
}
}
import 'package:angular/core/parser/parser_library.dart';
class _AST implements ParserAST {
bool get assignable => true;
}
class DartGetterSetterGen implements ParserBackend {
Map<String, bool> identifiers = {};
profiled(value, perf, text) => new _AST();
binaryFn(left, String fn, right) => new _AST();
unaryFn(String fn, right) => new _AST();
assignment(left, right, evalError) => new _AST();
multipleStatements(List statements) => new _AST();
arrayDeclaration(List elementFns) => new _AST();
objectIndex(obj, indexFn, evalError) => new _AST();
object(List keyValues) => new _AST();
fromOperator(String op) => new _AST();
value(v) => new _AST();
zero() => new _AST();
functionCall(fn, fnName, List argsFn, evalError) => new _AST();
fieldAccess(object, String field) {
identifiers[field] = true;
return new _AST();
}
getterSetter(String key) {
key.split('.').forEach((i) => identifiers[i] = true);
return new _AST();
}
}
class ParserGetterSetter {
DynamicParser parser;
DartGetterSetterGen backend;
ParserGetterSetter(DynamicParser this.parser, ParserBackend this.backend);
generateParser(List<String> exprs) {
exprs.forEach((expr) {
try {
parser(expr);
} catch (e) { }
});
print(generateCode(backend.identifiers.keys.toList()));
}
generateCode(List<String> keys) {
return '''
class StaticGetterSetter extends GetterSetter {
Map<String, Function> _getters = ${generateGetterMap(keys)};
Map<String, Function> _setters = ${generateSetterMap(keys)};
Function getter(String key) {
return _getters.containsKey(key) ? _getters[key] : super.getter(key);
}
Function setter(String key) {
return _setters.containsKey(key) ? _setters[key] : super.setter(key);
}
}
''';
}
generateGetterMap(List<String> keys) {
var lines = keys.map((key) => 'r"${key}": (s) => s.$key');
return '{\n ${lines.join(",\n ")}\n }';
}
generateSetterMap(List<String> keys) {
var lines = keys.map((key) => 'r"${key}": (s, v) => s.$key = v');
return '{\n ${lines.join(",\n ")}\n }';
}
}
part of angular.mock;
/**
* Class which simplifies bootstraping of angular for unit tests.
*
* Simply inject [TestBed] into the test, then use [compile] to
* match directives against the view.
*/
class TestBed {
final Injector injector;
final Scope rootScope;
final Compiler compiler;
final Parser parser;
Element rootElement;
List<Node> rootElements;
Block rootBlock;
TestBed(
Injector this.injector,
Scope this.rootScope,
Compiler this.compiler,
Parser this.parser);
/**
* Use to compile HTML and activete its directives.
*
* If [html] parametr is:
*
* - [String] then treat it as HTML
* - [Node] then treat it as the root node
* - [List<Node>] then treat it as a collection of nodse
*
* After the compilation the [rootElements] contains an array of compiled root nodes,
* and [rootElement] contains the first element from the [rootElemets].
*
*/
Element compile(html) {
if (html is String) {
rootElements = toNodeList(html);
} else if (html is Node) {
rootElements = [html];
} else if (html is List<Node>) {
rootElements = html;
} else {
throw 'Expecting: String, Node, or List<Node> got $html.';
}
rootElement = rootElements[0];
rootBlock = compiler(rootElements)(injector, rootElements);
return rootElement;
}
/**
* Convert an [html] String to a [List] of [Element]s.
*/
List<Element> toNodeList(html) {
var div = new DivElement();
div.setInnerHtml(html, treeSanitizer: new NullTreeSanitizer());
var nodes = [];
for(var node in div.nodes) {
nodes.add(node);
}
return nodes;
}
/**
* Triggern a specific DOM element on a given node to test directives
* which listen to events.
*/
triggerEvent(element, name, [type='MouseEvent']) {
element.dispatchEvent(new Event.eventType(type, name));
}
/**
* Select an [OPTION] in a [SELECT] with a given name and trigger the
* appropriate DOM event. Used when testing [SELECT] controlls in forms.
*/
selectOption(element, text) {
element.queryAll('option').forEach((o) => o.selected = o.text == text);
triggerEvent(element, 'change');
}
}
part of angular.core.dom;
class UrlRewriter {
String call(url) => url;
}
/**
* HTTP backend used by the [Http] service that delegates to dart:html's
* [HttpRequest] and deals with Dart bugs.
*
* Never use this service directly, instead use the higher-level [Http].
*
* During testing this implementation is swapped with [MockHttpBackend] which
* can be trained with responses.
*/
class HttpBackend {
/**
* Wrapper around dart:html's [HttpRequest.request]
*/
async.Future request(String url,
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(dom.ProgressEvent e)}) {
// Complete inside a then to work-around dartbug.com/13051
var c = new async.Completer();
dom.HttpRequest.request(url,
method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress).then((x) => c.complete(x));
return c.future;
}
}
typedef RequestInterceptor(HttpResponseConfig);
typedef RequestErrorInterceptor(dynamic);
typedef Response(HttpResponse);
typedef ResponseError(dynamic);
/**
* HttpInterceptors are used to modify the Http request. They can be added to
* [HttpInterceptors] or passed into [Http.call].
*/
class HttpInterceptor {
RequestInterceptor request;
Response response;
RequestErrorInterceptor requestError;
ResponseError responseError;
/**
* All parameters are optional.
*/
HttpInterceptor({
this.request, this.response,
this.requestError, this.responseError});
}
/**
* The default transform data interceptor.abstract
*
* For requests, this interceptor will
* automatically stringify any non-string non-file objects.
*
* For responses, this interceptor will unwrap JSON objects and
* parse them into [Map]s.
*/
class DefaultTransformDataHttpInterceptor implements HttpInterceptor {
Function request = (HttpResponseConfig config) {
if (config.data != null && config.data is! String && config.data is! dom.File) {
config.data = json.stringify(config.data);
}
return config;
};
static var _JSON_START = new RegExp(r'^\s*(\[|\{[^\{])');
static var _JSON_END = new RegExp(r'[\}\]]\s*$');
static var _PROTECTION_PREFIX = new RegExp('^\\)\\]\\}\',?\\n');
Function response = (HttpResponse r) {
if (r.data is String) {
var d = r.data;
d = d.replaceFirst(_PROTECTION_PREFIX, '');
if (d.contains(_JSON_START) && d.contains(_JSON_END)) {
d = json.parse(d);
}
return new HttpResponse.copy(r, data: d);
}
return r;
};
Function requestError, responseError;
}
/**
* A list of [HttpInterceptor]s.
*/
class HttpInterceptors {
List<HttpInterceptor> _interceptors = [new DefaultTransformDataHttpInterceptor()];
add(HttpInterceptor x) => _interceptors.add(x);
addAll(List<HttpInterceptor> x) => _interceptors.addAll(x);
/**
* Called from [Http] to construct a [Future] chain.
*/
constructChain(List chain) {
_interceptors.reversed.forEach((HttpInterceptor i) {
// AngularJS has an optimization of not including null interceptors.
chain.insert(0, [
i.request == null ? (x) => x : i.request,
i.requestError]);
chain.add([
i.response == null ? (x) => x : i.response,
i.responseError]);
});
}
/**
* Default constructor.
*/
HttpInterceptors() {
_interceptors = [new DefaultTransformDataHttpInterceptor()];
}
/**
* Creates a [HttpInterceptors] from a [List]. Does not include the default interceptors.
*/
HttpInterceptors.of([List interceptors]) {
_interceptors = interceptors;
}
}
/**
* The request configuration of the request associated with this response.
*/
class HttpResponseConfig {
/**
* The request's URL
*/
String url;
/**
* The request params as a Map
*/
Map params;
/**
* The header map without mangled keys
*/
Map headers;
var data;
var _headersObj;
/**
* Header accessor. Given a string, it will return the matching header,
* case-insentivitively. Without a string, returns a header object will
* upper-case keys.
*/
header([String name]) {
if (_headersObj == null) {
_headersObj = {};
headers.forEach((k,v) {
_headersObj[k.toLowerCase()] = v;
});
}
if (name != null) {
name = name.toLowerCase();
if (!_headersObj.containsKey(name)) return null;
return _headersObj[name];
}
return _headersObj;
}
/**
* Constructor
*/
HttpResponseConfig({this.url, this.params, this.headers, this.data});
}
/**
* The response for an HTTP request. Returned from the [Http] service.
*/
class HttpResponse {
/**
* The HTTP status code.
*/
int status;
/**
* DEPRECATED
*/
var responseText;
Map _headers;
/**
* The [HttpResponseConfig] object which contains the requested URL
*/
HttpResponseConfig config;
/**
* Constructor
*/
HttpResponse([this.status, this.responseText, this._headers, this.config]);
/**
* Copy constructor. Creates a clone of the response, optionally with new
* data.
*/
HttpResponse.copy(HttpResponse r, {data}) {
status = r.status;
responseText = data == null ? r.responseText : data;
_headers = r._headers == null ? null : new Map.from(r._headers);
config = r.config;
}
/**
* The response's data. Either a string or a transformed object.
*/
get data => responseText;
/**
* The response's headers. Without parameters, this method will return the
* [Map] of headers. With [key] parameter, this method will return the specific
* header.
*/
headers([String key]) {
if (key == null) {
return _headers;
}
if (_headers.containsKey(key)) {
return _headers[key];
}
return null;
}
/**
* Useful for debugging.
*/
toString() => 'HTTP $status: $data';
}
/**
* Default header configuration.
*/
class HttpDefaultHeaders {
static String _defaultContentType = 'application/json;charset=utf-8';
Map _headers = {
'COMMON': {
'Accept': 'application/json, text/plain, */*'
},
'POST' : {
'Content-Type': _defaultContentType
},
'PUT' : {
'Content-Type': _defaultContentType
},
'PATCH' : {
'Content-Type': _defaultContentType
}
};
_applyHeaders(method, ucHeaders, headers) {
if (!_headers.containsKey(method)) return;
_headers[method].forEach((k, v) {
if (!ucHeaders.contains(k.toUpperCase())) {
headers[k] = v;
}
});
}
/**
* Called from [Http], this method sets default headers on [headers]
*/
setHeaders(Map<String, String> headers, String method) {
assert(headers != null);
var ucHeaders = headers.keys.map((x) => x.toUpperCase()).toSet();
_applyHeaders('COMMON', ucHeaders, headers);
_applyHeaders(method.toUpperCase(), ucHeaders, headers);
}
/**
* Returns the default header [Map] for a method. You can then modify
* the map.
*
* Passing 'common' as [method] will return a Map that contains headers
* common to all operations.
*/
operator[](method) {
return _headers[method.toUpperCase()];
}
}
/**
* Injected into the [Http] service. This class contains application-wide
* HTTP defaults.
*
* The default implementation provides headers which the
* Angular team believes to be useful.
*/
class HttpDefaults {
/**
* The [HttpDefaultHeaders] object used by [Http] to add default headers
* to requests.
*/
HttpDefaultHeaders headers;
/**
* The default cache. To enable caching application-wide, instantiate with a
* [Cache] object.
*/
var cache;
/**
* Constructor intended for DI.
*/
HttpDefaults(HttpDefaultHeaders this.headers);
}
/**
* The [Http] service facilitates communication with the remote HTTP servers. It
* uses dart:html's [HttpRequest] and provides a number of features on top
* of the core Dart library.
*
* For unit testing, applications should use the [MockHttpBackend] service.
*
* # General usage
* The [call] method takes a number of named parameters and returns a
* [Future<HttpResponse>].
*
* http(method: 'GET', url: '/someUrl')
* .then((HttpResponse response) { .. },
* onError: (HttpRequest request) { .. });
*
* A response status code between 200 and 299 is considered a success status and
* will result in the 'then' being called. Note that if the response is a redirect,
* Dart's [HttpRequest] will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
* # Shortcut methods
*
* The Http service also defines a number of shortcuts:
*
* http.get('/someUrl') is the same as http(method: 'GET', url: '/someUrl')
*
* See the method definitions below.
*
* # Setting HTTP Headers
*
* The [Http] service will add certain HTTP headers to requests. These defaults
* can be configured using the [HttpDefaultHeaders] object. The defaults are:
*
* - For all requests: `Accept: application/json, text/plain, * / *`
* - For POST, PUT, PATCH requests: `Content-Type: application/json`
*
* # Caching
*
* To enable caching, pass a [Cache] object into the [call] method. The [Http]
* service will store responses in the cache and return the response for
* any matching requests.
*
* Note that data is returned through a [Future], regardless of whether it
* came from the [Cache] or the server.
*
* If there are multiple GET requests for the same not-yet-in-cache URL
* while a cache is in use, only one request to the server will be made.
*
* # Interceptors
*
* Http uses the interceptors from [HttpInterceptors]. You can also include
* interceptors in the [call] method.
*
* # Security Considerations
*
* NOTE: < not yet documented >
*/
class Http {
Map<String, async.Future<HttpResponse>> _pendingRequests = <String, async.Future<HttpResponse>>{};
UrlRewriter _rewriter;
HttpBackend _backend;
HttpInterceptors _interceptors;
/**
* The defaults for [Http]
*/
HttpDefaults defaults;
/**
* Constructor, useful for DI.
*/
Http(UrlRewriter this._rewriter,
HttpBackend this._backend,
HttpDefaults this.defaults,
HttpInterceptors this._interceptors);
/**
* DEPRECATED
*/
async.Future<String> getString(String url,
{bool withCredentials, void onProgress(dom.ProgressEvent e), Cache cache}) {
return request(url,
withCredentials: withCredentials,
onProgress: onProgress,
cache: cache).then((HttpResponse xhr) => xhr.responseText);
}
/**
* Returns a [Future<HttpResponse>] when the request is fulfilled.
*
* Named Parameters:
* - method: HTTP method (e.g. 'GET', 'POST', etc)
* - url: Absolute or relative URL of the resource being requested.
* - data: Data to be sent as the request message data.
* - params: Map of strings or objects which will be turned to
* `?key1=value1&key2=value2` after the url. If the values are
* not strings, they will be JSONified.
* - headers: Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function
* is null, the header will not be sent.
* - xsrfHeaderName: TBI
* - xsrfCookieName: TBI
* - interceptors: Either a [HttpInterceptor] or a [HttpInterceptors]
* - cache: Boolean or [Cache]. If true, the default cache will be used.
* - timeout: deprecated
*/
async.Future<HttpResponse> call({
String url,
String method,
data,
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) {
if (xsrfHeaderName != null || xsrfCookieName != null ||
timeout != null) {
throw ['not implemented'];
}
method = method.toUpperCase();
if (headers == null) { headers = {}; }
defaults.headers.setHeaders(headers, method);
// Check for functions in headers
headers.forEach((k,v) {
if (v is Function) {
headers[k] = v();
}
});
var serverRequest = (HttpResponseConfig config) {
assert(config.data == null || config.data is String || config.data is dom.File);
// Strip content-type if data is undefined
if (config.data == null) {
List<String> toRemove = [];
headers.forEach((h, _) {
if (h.toUpperCase() == 'CONTENT-TYPE') {
toRemove.add(h);
};
});
toRemove.forEach((x) => headers.remove(x));
}
return request(
null,
config: config,
method: method,
sendData: config.data,
requestHeaders: config.headers,
cache: cache);
};
var chain = [[serverRequest, null]];
var future = new async.Future.value(new HttpResponseConfig(
url: url,
params: params,
headers: headers,
data: data));
_interceptors.constructChain(chain);
if (interceptors != null) {
if (interceptors is HttpInterceptor) {
interceptors = new HttpInterceptors.of([interceptors]);
}
assert(interceptors is HttpInterceptors);
interceptors.constructChain(chain);
}
chain.forEach((chainFns) {
future = future.then(chainFns[0], onError: chainFns[1]);
});
return future;
}
/**
* Shortcut method for GET requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> get(String url, {
String data,
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'GET', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Shortcut method for DELETE requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> delete(String url, {
String data,
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'DELETE', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Shortcut method for HEAD requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> head(String url, {
String data,
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'HEAD', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Shortcut method for PUT requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> put(String url, String data, {
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'PUT', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Shortcut method for POST requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> post(String url, String data, {
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'POST', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Shortcut method for JSONP requests. See [call] for a complete description
* of parameters.
*/
async.Future<HttpResponse> jsonp(String url, {
String data,
Map<String, dynamic> params,
Map<String, String> headers,
xsrfHeaderName,
xsrfCookieName,
interceptors,
cache,
timeout
}) => call(method: 'JSONP', url: url, data: data, params: params, headers: headers,
xsrfHeaderName: xsrfHeaderName, xsrfCookieName: xsrfCookieName,
interceptors: interceptors,
cache: cache, timeout: timeout);
/**
* Parse raw headers into key-value object
*/
static Map<String, String> parseHeaders(dom.HttpRequest value) {
var headers = value.getAllResponseHeaders();
var parsed = {}, key, val, i;
if (headers == null) return parsed;
headers.split('\n').forEach((line) {
i = line.indexOf(':');
if (i == -1) return;
key = line.substring(0, i).trim().toLowerCase();
val = line.substring(i + 1).trim();
if (key != '') {
if (parsed.containsKey(key)) {
parsed[key] += ', ' + val;
} else {
parsed[key] = val;
}
}
});
return parsed;
}
/**
* Returns an [Iterable] of [Future] [HttpResponse]s for the requests
* that the [Http] service is currently waiting for.
*/
Iterable<async.Future<HttpResponse> > get pendingRequests {
return _pendingRequests.values;
}
/**
* DEPRECATED
*/
async.Future<HttpResponse> request(String rawUrl,
{ HttpResponseConfig config,
String method: 'GET',
bool withCredentials: false,
String responseType,
String mimeType,
Map<String, String> requestHeaders,
sendData,
void onProgress(dom.ProgressEvent e),
/*Cache<String, HttpResponse> or false*/ cache }) {
String url;
if (config == null) {
url = _rewriter(rawUrl);
config = new HttpResponseConfig(url: url);
} else {
url = _buildUrl(config.url, config.params);
}
if (cache is bool && cache == false) {
cache = null;
} else if (cache == null) {
cache = defaults.cache;
}
// We return a pending request only if caching is enabled.
if (cache != null && _pendingRequests.containsKey(url)) {
return _pendingRequests[url];
}
var cachedValue = (cache != null && method == 'GET') ? cache.get(url) : null;
if (cachedValue != null) {
return new async.Future.value(new HttpResponse.copy(cachedValue));
}
var result = _backend.request(url,
method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress).then((dom.HttpRequest value) {
// TODO: Uncomment after apps migrate off of this class.
// assert(value.status >= 200 && value.status < 300);
var response = new HttpResponse(
value.status, value.responseText, parseHeaders(value),
config);
if (cache != null) {
cache.put(url, response);
}
_pendingRequests.remove(url);
return response;
}, onError: (error) {
if (error is! dom.ProgressEvent) {
throw error;
}
dom.ProgressEvent event = error;
_pendingRequests.remove(url);
dom.HttpRequest request = event.currentTarget;
return new async.Future.error(
new HttpResponse(request.status, request.response,
parseHeaders(request), config));
});
_pendingRequests[url] = result;
return result;
}
_buildUrl(String url, Map<String, dynamic> params) {
if (params == null) return url;
var parts = [];
new List.from(params.keys)..sort()..forEach((String key) {
var value = params[key];
if (value == null) return;
if (value is! List) value = [value];
value.forEach((v) {
if (v is Map) {
v = json.stringify(v);
}
parts.add(_encodeUriQuery(key) + '=' +
_encodeUriQuery("$v"));
});
});
return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
}
_encodeUriQuery(val, {bool pctEncodeSpaces: false}) =>
Uri.encodeComponent(val)
.replaceAll('%40', '@')
.replaceAll('%3A', ':')
.replaceAll('%24', r'$')
.replaceAll('%2C', ',')
.replaceAll('%20', pctEncodeSpaces ? '%20' : '+');
}
part of angular.core.parser;
typedef ParsedGetter(self, [locals]);
typedef ParsedSetter(self, value, [locals]);
typedef Getter([locals]);
typedef Setter(value, [locals]);
abstract class ParserAST {
bool get assignable;
}
class Token {
final int index;
final String text;
var value;
// Tokens should have one of these set.
String opKey;
String key;
Token(this.index, this.text);
withOp(op) {
this.opKey = op;
}
withGetterSetter(key) {
this.key = key;
}
withValue(value) { this.value = value; }
toString() => "Token($text)";
}
typedef Operator(dynamic self, Map<String, dynamic>locals, ParserAST a, ParserAST b);
Operator NULL_OP = (_, _x, _0, _1) => null;
Operator NOT_IMPL_OP = (_, _x, _0, _1) { throw "Op not implemented"; };
// FUNCTIONS USED AT RUNTIME.
parserEvalError(String s, String text, stack) =>
['Eval Error: $s while evaling [$text]' +
(stack != null ? '\n\nFROM:\n$stack' : '')];
// Automatic type conversion.
autoConvertAdd(a, b) {
if (a != null && b != null) {
// TODO(deboer): Support others.
if (a is String && b is! String) {
return a + b.toString();
}
if (a is! String && b is String) {
return a.toString() + b;
}
return a + b;
}
if (a != null) return a;
if (b != null) return b;
return null;
}
objectIndexGetField(o, i, evalError) {
if (o == null) throw evalError('Accessing null object');
if (o is List) {
return o[i.toInt()];
} else if (o is Map) {
return o[i.toString()]; // toString dangerous?
}
throw evalError("Attempted field access on a non-list, non-map");
}
objectIndexSetField(o, i, v, evalError) {
if (o is List) {
int arrayIndex = i.toInt();
if (o.length <= arrayIndex) { o.length = arrayIndex + 1; }
o[arrayIndex] = v;
} else if (o is Map) {
o[i.toString()] = v; // toString dangerous?
} else {
throw evalError("Attempting to set a field on a non-list, non-map");
}
return v;
}
safeFunctionCall(userFn, fnName, evalError) {
if (userFn == null) {
throw evalError("Undefined function $fnName");
}
if (userFn is! Function) {
throw evalError("$fnName is not a function");
}
return userFn;
}
Map<String, Operator> OPERATORS = {
'undefined': NULL_OP,
'null': NULL_OP,
'true': (self, locals, a, b) => true,
'false': (self, locals, a, b) => false,
'+': (self, locals, aFn, bFn) {
var a = aFn.eval(self, locals);
var b = bFn.eval(self, locals);
return autoConvertAdd(a, b);
},
'-': (self, locals, a, b) {
assert(a != null || b != null);
var aResult = a != null ? a.eval(self, locals) : null;
var bResult = b != null ? b.eval(self, locals) : null;
return (aResult == null ? 0 : aResult) - (bResult == null ? 0 : bResult);
},
'*': (s, l, a, b) => a.eval(s, l) * b.eval(s, l),
'/': (s, l, a, b) => a.eval(s, l) / b.eval(s, l),
'%': (s, l, a, b) => a.eval(s, l) % b.eval(s, l),
'^': (s, l, a, b) => a.eval(s, l) ^ b.eval(s, l),
'=': NULL_OP,
'==': (s, l, a, b) => a.eval(s, l) == b.eval(s, l),
'!=': (s, l, a, b) => a.eval(s, l) != b.eval(s, l),
'<': (s, l, a, b) => a.eval(s, l) < b.eval(s, l),
'>': (s, l, a, b) => a.eval(s, l) > b.eval(s, l),
'<=': (s, l, a, b) => a.eval(s, l) <= b.eval(s, l),
'>=': (s, l, a, b) => a.eval(s, l) >= b.eval(s, l),
'&&': (s, l, a, b) => toBool(a.eval(s, l)) && toBool(b.eval(s, l)),
'||': (s, l, a, b) => toBool(a.eval(s, l)) || toBool(b.eval(s, l)),
'&': (s, l, a, b) => a.eval(s, l) & b.eval(s, l),
'|': NOT_IMPL_OP, //b(locals)(locals, a(locals))
'!': (self, locals, a, b) => !toBool(a.eval(self, locals))
};
class DynamicParser implements Parser {
final Profiler _perf;
final Lexer _lexer;
final ParserBackend _b;
DynamicParser(Profiler this._perf, Lexer this._lexer, ParserBackend this._b);
List<Token> _tokens;
String _text;
var _evalError;
ParserAST call(String text) {
try {
if (text == null) text = '';
_tokenSavers = [];
_text = text;
_tokens = _lexer.call(text);
_evalError = (String s, [stack]) => parserEvalError(s, text, stack);
ParserAST value = _statements();
if (_tokens.length != 0) {
throw _parserError("Unconsumed token ${_tokens[0].text}");
}
return _perf == null ? value : _b.profiled(value, _perf, text);
} finally {
_tokens = null;
_text = null;
_evalError = null;
_tokenSavers = null;
}
}
primaryFromToken(Token token, parserError) {
if (token.key != null) {
return _b.getterSetter(token.key);
}
if (token.opKey != null) {
return _b.fromOperator(token.opKey);
}
if (token.value != null) {
return _b.value(token.value);
}
if (token.text != null) {
return _b.value(token.text);
}
throw parserError("Internal Angular Error: Tokens should have keys, text or fns");
}
_parserError(String s, [Token t]) {
if (t == null && !_tokens.isEmpty) t = _tokens[0];
String location = t == null ?
'the end of the expression' :
'at column ${t.index + 1} in';
return 'Parser Error: $s $location [$_text]';
}
Token _peekToken() {
if (_tokens.length == 0)
throw "Unexpected end of expression: " + _text;
return _tokens[0];
}
Token _peek([String e1, String e2, String e3, String e4]) {
if (_tokens.length > 0) {
Token token = _tokens[0];
String t = token.text;
if (t==e1 || t==e2 || t==e3 || t==e4 ||
(e1 == null && e2 == null && e3 == null && e4 == null)) {
return token;
}
}
return null;
}
/**
* Token savers are synchronous lists that allows Parser functions to
* access the tokens parsed during some amount of time. They are useful
* for printing helpful debugging messages.
*/
List<List<Token>> _tokenSavers;
List<Token> _saveTokens() { var n = []; _tokenSavers.add(n); return n; }
_stopSavingTokens(x) { if (!_tokenSavers.remove(x)) { throw 'bad token saver'; } return x; }
_tokensText(List x) => x.map((x) => x.text).join();
Token _expect([String e1, String e2, String e3, String e4]){
Token token = _peek(e1, e2, e3, e4);
if (token != null) {
var consumed = _tokens.removeAt(0);
_tokenSavers.forEach((ts) => ts.add(consumed));
return token;
}
return null;
}
ParserAST _consume(e1){
if (_expect(e1) == null) {
throw _parserError("Missing expected $e1");
}
}
ParserAST _primary() {
var primary;
var ts = _saveTokens();
if (_expect('(') != null) {
primary = _filterChain();
_consume(')');
} else if (_expect('[') != null) {
primary = _arrayDeclaration();
} else if (_expect('{') != null) {
primary = _object();
} else {
Token token = _expect();
primary = primaryFromToken(token, _parserError);
if (primary == null) {
throw _parserError("Internal Angular Error: Unreachable code A.");
}
}
var next;
while ((next = _expect('(', '[', '.')) != null) {
if (next.text == '(') {
primary = _functionCall(primary, _tokensText(ts.sublist(0, ts.length - 1)));
} else if (next.text == '[') {
primary = _objectIndex(primary);
} else if (next.text == '.') {
primary = _fieldAccess(primary);
} else {
throw _parserError("Internal Angular Error: Unreachable code B.");
}
}
_stopSavingTokens(ts);
return primary;
}
ParserAST _binaryFn(ParserAST left, String op, ParserAST right) =>
_b.binaryFn(left, op, right);
ParserAST _unaryFn(String op, ParserAST right) =>
_b.unaryFn(op, right);
ParserAST _unary() {
var token;
if (_expect('+') != null) {
return _primary();
} else if ((token = _expect('-')) != null) {
return _binaryFn(_b.zero(), token.opKey, _unary());
} else if ((token = _expect('!')) != null) {
return _unaryFn(token.opKey, _unary());
} else {
return _primary();
}
}
ParserAST _multiplicative() {
var left = _unary();
var token;
while ((token = _expect('*','/','%')) != null) {
left = _binaryFn(left, token.opKey, _unary());
}
return left;
}
ParserAST _additive() {
var left = _multiplicative();
var token;
while ((token = _expect('+','-')) != null) {
left = _binaryFn(left, token.opKey, _multiplicative());
}
return left;
}
ParserAST _relational() {
var left = _additive();
var token;
if ((token = _expect('<', '>', '<=', '>=')) != null) {
left = _binaryFn(left, token.opKey, _relational());
}
return left;
}
ParserAST _equality() {
var left = _relational();
var token;
if ((token = _expect('==','!=')) != null) {
left = _binaryFn(left, token.opKey, _equality());
}
return left;
}
ParserAST _logicalAND() {
var left = _equality();
var token;
if ((token = _expect('&&')) != null) {
left = _binaryFn(left, token.opKey, _logicalAND());
}
return left;
}
ParserAST _logicalOR() {
var left = _logicalAND();
var token;
while(true) {
if ((token = _expect('||')) != null) {
left = _binaryFn(left, token.opKey, _logicalAND());
} else {
return left;
}
}
}
ParserAST _assignment() {
var ts = _saveTokens();
var left = _logicalOR();
_stopSavingTokens(ts);
var right;
var token;
if ((token = _expect('=')) != null) {
if (!left.assignable) {
throw _parserError('Expression ${_tokensText(ts)} is not assignable', token);
}
right = _logicalOR();
return _b.assignment(left, right, _evalError);
} else {
return left;
}
}
ParserAST _expression() {
return _assignment();
}
_filterChain() {
var left = _expression();
var token;
while(true) {
if ((token = _expect('|')) != null) {
left = _filter(left);
} else {
return left;
}
}
}
ParserAST _filter(ParserAST left) {
var token = _expect();
var filterName = token.text;
var argsFn = [];
while(true) {
if ((token = _expect(':')) != null) {
argsFn.add(_expression());
} else {
return _b.filter(filterName, left, argsFn, _evalError);
}
}
}
_statements() {
List<ParserAST> statements = [];
while (true) {
if (_tokens.length > 0 && _peek('}', ')', ';', ']') == null)
statements.add(_filterChain());
if (_expect(';') == null) {
return statements.length == 1
? statements[0]
: _b.multipleStatements(statements);
}
}
}
_functionCall(fn, fnName) {
var argsFn = [];
if (_peekToken().text != ')') {
do {
argsFn.add(_expression());
} while (_expect(',') != null);
}
_consume(')');
return _b.functionCall(fn, fnName, argsFn, _evalError);
}
// This is used with json array declaration
_arrayDeclaration() {
var elementFns = [];
if (_peekToken().text != ']') {
do {
elementFns.add(_expression());
} while (_expect(',') != null);
}
_consume(']');
return _b.arrayDeclaration(elementFns);
}
_objectIndex(obj) {
var indexFn = _expression();
_consume(']');
return _b.objectIndex(obj, indexFn, _evalError);
}
_fieldAccess(object) {
var field = _expect().text;
//var getter = getter(field);
return _b.fieldAccess(object, field);
}
_object() {
var keyValues = [];
if (_peekToken().text != '}') {
do {
var token = _expect(),
key = token.value != null && token.value is String ? token.value : token.text;
_consume(":");
var value = _expression();
keyValues.add({"key":key, "value":value});
} while (_expect(',') != null);
}
_consume('}');
return _b.object(keyValues);
}
}
part of angular.directive;
/**
* Base class for NgIfAttrDirective and NgUnlessAttrDirective.
*/
abstract class _NgUnlessIfAttrDirectiveBase {
final BoundBlockFactory _boundBlockFactory;
final BlockHole _blockHole;
final Scope _scope;
Block _block;
/**
* The new child scope. This child scope is recreated whenever the `ng-if`
* subtree is inserted into the DOM and destroyed when it's removed from the
* DOM. Refer
* https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance
*/
Scope _childScope;
_NgUnlessIfAttrDirectiveBase(BoundBlockFactory this._boundBlockFactory,
BlockHole this._blockHole,
Scope this._scope);
// Override in subclass.
set condition(value);
void _ensureBlockExists() {
if (_block == null) {
_childScope = _scope.$new();
_block = _boundBlockFactory(_childScope);
_block.insertAfter(_blockHole);
}
}
void _ensureBlockDestroyed() {
if (_block != null) {
_block.remove();
_childScope.$destroy();
_block = null;
_childScope = null;
}
}
}
/**
* The `ng-if` directive compliments the `ng-unless` (provided by
* [NgUnlessAttrDirective]) directive.
*
* directive based on the **truthy/falsy** value of the provided expression.
* Specifically, if the expression assigned to `ng-if` evaluates to a `false`
* value, then the subtree is removed from the DOM. Otherwise, *a clone of the
* subtree* is reinserted into the DOM. This clone is created from the compiled
* state. As such, modifications made to the element after compilation (e.g.
* changing the `class`) are lost when the element is destroyed.
*
* Whenever the subtree is inserted into the DOM, it always gets a new child
* scope. This child scope is destroyed when the subtree is removed from the
* DOM. Refer
* https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance
*
* This has an important implication when `ng-model` is used inside an `ng-if`
* to bind to a javascript primitive defined in the parent scope. In such a
* situation, any modifications made to the variable in the `ng-if` subtree will
* be made on the child scope and override (hide) the value in the parent scope.
* The parent scope will remain unchanged by changes affected by this subtree.
*
* Note: `ng-if` differs from `ng-show` and `ng-hide` in that `ng-if` completely
* removes and recreates the element in the DOM rather than changing its
* visibility via the `display` css property. A common case when this
* difference is significant is when using css selectors that rely on an
* element's position within the DOM (HTML), such as the `:first-child` or
* `:last-child` pseudo-classes.
*
* Example:
*
* <!-- By using ng-if instead of ng-show, we avoid the cost of the showdown
* filter, the repeater, etc. -->
* <div ng-if="showDetails">
* {{obj.details.markdownText | showdown}}
* <div ng-repeat="item in obj.details.items">
* ...
* </div>
* </div>
*/
@NgDirective(
children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector:'[ng-if]',
map: const {'.': '=.condition'})
class NgIfDirective extends _NgUnlessIfAttrDirectiveBase {
NgIfDirective(BoundBlockFactory boundBlockFactory,
BlockHole blockHole,
Scope scope): super(boundBlockFactory, blockHole, scope);
set condition(value) =>
(toBool(value) ? _ensureBlockExists() : _ensureBlockDestroyed());
}
/**
* The `ng-unless` directive compliments the `ng-if` (provided by
* [NgIfAttrDirective]) directive.
*
* The `ng-unless` directive recreates/destroys the DOM subtree containing the
* directive based on the **falsy/truthy** value of the provided expression.
* Specifically, if the expression assigned to `ng-unless` evaluates to a `true`
* value, then the subtree is removed from the DOM. Otherwise, *a clone of the
* subtree* is reinserted into the DOM. This clone is created from the compiled
* state. As such, modifications made to the element after compilation (e.g.
* changing the `class`) are lost when the element is destroyed.
*
* Whenever the subtree is inserted into the DOM, it always gets a new child
* scope. This child scope is destroyed when the subtree is removed from the
* DOM. Refer
* https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance prototypal inheritance
*
* This has an important implication when `ng-model` is used inside an
* `ng-unless` to bind to a javascript primitive defined in the parent scope.
* In such a situation, any modifications made to the variable in the
* `ng-unless` subtree will be made on the child scope and override (hide) the
* value in the parent scope. The parent scope will remain unchanged by changes
* affected by this subtree.
*
* Note: `ng-unless` differs from `ng-show` and `ng-hide` in that `ng-unless`
* completely removes and recreates the element in the DOM rather than changing
* its visibility via the `display` css property. A common case when this
* difference is significant is when using css selectors that rely on an
* element's position within the DOM (HTML), such as the `:first-child` or
* `:last-child` pseudo-classes.
*
* Example:
*
* <!-- By using ng-unless instead of ng-show, we avoid the cost of the showdown
* filter, the repeater, etc. -->
* <div ng-unless="terseView">
* {{obj.details.markdownText | showdown}}
* <div ng-repeat="item in obj.details.items">
* ...
* </div>
* </div>
*/
@NgDirective(
children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector:'[ng-unless]',
map: const {'.': '=.condition'})
class NgUnlessDirective extends _NgUnlessIfAttrDirectiveBase {
NgUnlessDirective(BoundBlockFactory boundBlockFactory,
BlockHole blockHole,
Scope scope): super(boundBlockFactory, blockHole, scope);
set condition(value) =>
(!toBool(value) ? _ensureBlockExists() : _ensureBlockDestroyed());
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
/**
* Provides the ability to format a number in a locale-specific way. The
* format is specified as a pattern using a subset of the ICU formatting
* patterns.
*
* - `0` A single digit
* - `#` A single digit, omitted if the value is zero
* - `.` Decimal separator
* - `-` Minus sign
* - `,` Grouping separator
* - `E` Separates mantissa and expontent
* - `+` - Before an exponent, indicates it should be prefixed with a plus sign.
* - `%` - In prefix or suffix, multiply by 100 and show as percentage
* - `‰ (\u2030)` In prefix or suffix, multiply by 1000 and show as per mille
* - `¤ (\u00A4)` Currency sign, replaced by currency name
* - `'` Used to quote special characters
* - `;` Used to separate the positive and negative patterns if both are present
*
* For example,
* var f = new NumberFormat("###.0#", "en_US");
* print(f.format(12.345));
* ==> 12.34
* If the locale is not specified, it will default to the current locale. If
* the format is not specified it will print in a basic format with at least
* one integer digit and three fraction digits.
*
* There are also standard patterns available via the special constructors. e.g.
* var symbols = new NumberFormat.percentFormat("ar");
* There are four such constructors: decimalFormat, percentFormat,
* scientificFormat and currencyFormat. However, at the moment,
* scientificFormat prints only as equivalent to "#E0" and does not take
* into account significant digits. currencyFormat will always use the name
* of the currency rather than the symbol.
*/
class NumberFormat {
/** Variables to determine how number printing behaves. */
// TODO(alanknight): If these remain as variables and are set based on the
// pattern, can we make them final?
String _negativePrefix = '-';
String _positivePrefix = '';
String _negativeSuffix = '';
String _positiveSuffix = '';
/**
* How many numbers in a group when using punctuation to group digits in
* large numbers. e.g. in en_US: "1,000,000" has a grouping size of 3 digits
* between commas.
*/
int _groupingSize = 3;
bool _decimalSeparatorAlwaysShown = false;
bool _useSignForPositiveExponent = false;
bool _useExponentialNotation = false;
int maximumIntegerDigits = 40;
int minimumIntegerDigits = 1;
int maximumFractionDigits = 3;
int minimumFractionDigits = 0;
int minimumExponentDigits = 0;
int _multiplier = 1;
/**
* Stores the pattern used to create this format. This isn't used, but
* is helpful in debugging.
*/
String _pattern;
/** The locale in which we print numbers. */
final String _locale;
/** Caches the symbols used for our locale. */
NumberSymbols _symbols;
/**
* Transient internal state in which to build up the result of the format
* operation. We can have this be just an instance variable because Dart is
* single-threaded and unless we do an asynchronous operation in the process
* of formatting then there will only ever be one number being formatted
* at a time. In languages with threads we'd need to pass this on the stack.
*/
StringBuffer _buffer;
/**
* Create a number format that prints using [newPattern] as it applies in
* [locale].
*/
factory NumberFormat([String newPattern, String locale]) {
return new NumberFormat._forPattern(locale, (x) => newPattern);
}
/** Create a number format that prints as DECIMAL_PATTERN. */
NumberFormat.decimalPattern([String locale]) :
this._forPattern(locale, (x) => x.DECIMAL_PATTERN);
/** Create a number format that prints as PERCENT_PATTERN. */
NumberFormat.percentPattern([String locale]) :
this._forPattern(locale, (x) => x.PERCENT_PATTERN);
/** Create a number format that prints as SCIENTIFIC_PATTERN. */
NumberFormat.scientificPattern([String locale]) :
this._forPattern(locale, (x) => x.SCIENTIFIC_PATTERN);
/** Create a number format that prints as CURRENCY_PATTERN. */
NumberFormat.currencyPattern([String locale]) :
this._forPattern(locale, (x) => x.CURRENCY_PATTERN);
/**
* Create a number format that prints in a pattern we get from
* the [getPattern] function using the locale [locale].
*/
NumberFormat._forPattern(String locale, Function getPattern) :
_locale = Intl.verifiedLocale(locale, localeExists) {
_symbols = numberFormatSymbols[_locale];
_setPattern(getPattern(_symbols));
}
/**
* Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
*/
String get locale => _locale;
/**
* Return true if the locale exists, or if it is null. The null case
* is interpreted to mean that we use the default locale.
*/
static bool localeExists(localeName) {
if (localeName == null) return false;
return numberFormatSymbols.containsKey(localeName);
}
/**
* Return the symbols which are used in our locale. Cache them to avoid
* repeated lookup.
*/
NumberSymbols get symbols {
return _symbols;
}
/**
* Format [number] according to our pattern and return the formatted string.
*/
String format(num number) {
// TODO(alanknight): Do we have to do anything for printing numbers bidi?
// Or are they always printed left to right?
if (number.isNaN) return symbols.NAN;
if (number.isInfinite) return "${_signPrefix(number)}${symbols.INFINITY}";
_newBuffer();
_add(_signPrefix(number));
_formatNumber(number.abs() * _multiplier);
_add(_signSuffix(number));
var result = _buffer.toString();
_buffer = null;
return result;
}
/**
* Format the main part of the number in the form dictated by the pattern.
*/
void _formatNumber(num number) {
if (_useExponentialNotation) {
_formatExponential(number);
} else {
_formatFixed(number);
}
}
/** Format the number in exponential notation. */
void _formatExponential(num number) {
if (number == 0.0) {
_formatFixed(number);
_formatExponent(0);
return;
}
var exponent = (log(number) / log(10)).floor();
var mantissa = number / pow(10.0, exponent);
var minIntDigits = minimumIntegerDigits;
if (maximumIntegerDigits > 1 &&
maximumIntegerDigits > minimumIntegerDigits) {
// A repeating range is defined; adjust to it as follows.
// If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
// -3,-4,-5=>-6, etc. This takes into account that the
// exponent we have here is off by one from what we expect;
// it is for the format 0.MMMMMx10^n.
while ((exponent % maximumIntegerDigits) != 0) {
mantissa *= 10;
exponent--;
}
minIntDigits = 1;
} else {
// No repeating range is defined, use minimum integer digits.
if (minimumIntegerDigits < 1) {
exponent++;
mantissa /= 10;
} else {
exponent -= minimumIntegerDigits - 1;
mantissa *= pow(10, minimumIntegerDigits - 1);
}
}
_formatFixed(mantissa);
_formatExponent(exponent);
}
/**
* Format the exponent portion, e.g. in "1.3e-5" the "e-5".
*/
void _formatExponent(num exponent) {
_add(symbols.EXP_SYMBOL);
if (exponent < 0) {
exponent = -exponent;
_add(symbols.MINUS_SIGN);
} else if (_useSignForPositiveExponent) {
_add(symbols.PLUS_SIGN);
}
_pad(minimumExponentDigits, exponent.toString());
}
/** Used to test if we have exceeded Javascript integer limits. */
final _maxInt = pow(2, 52);
/**
* Format the basic number portion, inluding the fractional digits.
*/
void _formatFixed(num number) {
// Very fussy math to get integer and fractional parts.
var power = pow(10, maximumFractionDigits);
var shiftedNumber = (number * power);
// We must not roundToDouble() an int or it will lose precision. We must not
// round() a large double or it will take its loss of precision and
// preserve it in an int, which we will then print to the right
// of the decimal place. Therefore, only roundToDouble if we are already
// a double.
if (shiftedNumber is double) {
shiftedNumber = shiftedNumber.roundToDouble();
}
var intValue, fracValue;
if (shiftedNumber.isInfinite) {
intValue = number.toInt();
fracValue = 0;
} else {
intValue = shiftedNumber.round() ~/ power;
fracValue = (shiftedNumber - intValue * power).floor();
}
var fractionPresent = minimumFractionDigits > 0 || fracValue > 0;
// If the int part is larger than 2^52 and we're on Javascript (so it's
// really a float) it will lose precision, so pad out the rest of it
// with zeros. Check for Javascript by seeing if an integer is double.
var paddingDigits = new StringBuffer();
if (1 is double && intValue > _maxInt) {
var howManyDigitsTooBig = (log(intValue) / LN10).ceil() - 16;
var divisor = pow(10, howManyDigitsTooBig).round();
for (var each in new List(howManyDigitsTooBig.toInt())) {
paddingDigits.write(symbols.ZERO_DIGIT);
}
intValue = (intValue / divisor).truncate();
}
var integerDigits = "${intValue}${paddingDigits}".codeUnits;
var digitLength = integerDigits.length;
if (_hasPrintableIntegerPart(intValue)) {
_pad(minimumIntegerDigits - digitLength);
for (var i = 0; i < digitLength; i++) {
_addDigit(integerDigits[i]);
_group(digitLength, i);
}
} else if (!fractionPresent) {
// If neither fraction nor integer part exists, just print zero.
_addZero();
}
_decimalSeparator(fractionPresent);
_formatFractionPart((fracValue + power).toString());
}
/**
* Format the part after the decimal place in a fixed point number.
*/
void _formatFractionPart(String fractionPart) {
var fractionCodes = fractionPart.codeUnits;
var fractionLength = fractionPart.length;
while(fractionCodes[fractionLength - 1] == _zero &&
fractionLength > minimumFractionDigits + 1) {
fractionLength--;
}
for (var i = 1; i < fractionLength; i++) {
_addDigit(fractionCodes[i]);
}
}
/** Print the decimal separator if appropriate. */
void _decimalSeparator(bool fractionPresent) {
if (_decimalSeparatorAlwaysShown || fractionPresent) {
_add(symbols.DECIMAL_SEP);
}
}
/**
* Return true if we have a main integer part which is printable, either
* because we have digits left of the decimal point, or because there are
* a minimum number of printable digits greater than 1.
*/
bool _hasPrintableIntegerPart(int intValue) {
return intValue > 0 || minimumIntegerDigits > 0;
}
/**
* Create a new empty buffer. See comment on [_buffer] variable for why
* we have it as an instance variable rather than passing it on the stack.
*/
void _newBuffer() { _buffer = new StringBuffer(); }
/** A group of methods that provide support for writing digits and other
* required characters into [_buffer] easily.
*/
void _add(String x) { _buffer.write(x);}
void _addCharCode(int x) { _buffer.writeCharCode(x); }
void _addZero() { _buffer.write(symbols.ZERO_DIGIT); }
void _addDigit(int x) { _buffer.writeCharCode(_localeZero + x - _zero); }
/** Print padding up to [numberOfDigits] above what's included in [basic]. */
void _pad(int numberOfDigits, [String basic = '']) {
for (var i = 0; i < numberOfDigits - basic.length; i++) {
_add(symbols.ZERO_DIGIT);
}
for (var x in basic.codeUnits) {
_addDigit(x);
}
}
/**
* We are printing the digits of the number from left to right. We may need
* to print a thousands separator or other grouping character as appropriate
* to the locale. So we find how many places we are from the end of the number
* by subtracting our current [position] from the [totalLength] and print
* the separator character every [_groupingSize] digits.
*/
void _group(int totalLength, int position) {
var distanceFromEnd = totalLength - position;
if (distanceFromEnd <= 1 || _groupingSize <= 0) return;
if (distanceFromEnd % _groupingSize == 1) {
_add(symbols.GROUP_SEP);
}
}
/** Returns the code point for the character '0'. */
final _zero = '0'.codeUnits.first;
/** Returns the code point for the locale's zero digit. */
// Note that there is a slight risk of a locale's zero digit not fitting
// into a single code unit, but it seems very unlikely, and if it did,
// there's a pretty good chance that our assumptions about being able to do
// arithmetic on it would also be invalid.
get _localeZero => symbols.ZERO_DIGIT.codeUnits.first;
/**
* Returns the prefix for [x] based on whether it's positive or negative.
* In en_US this would be '' and '-' respectively.
*/
String _signPrefix(num x) {
return x.isNegative ? _negativePrefix : _positivePrefix;
}
/**
* Returns the suffix for [x] based on wether it's positive or negative.
* In en_US there are no suffixes for positive or negative.
*/
String _signSuffix(num x) {
return x.isNegative ? _negativeSuffix : _positiveSuffix;
}
void _setPattern(String newPattern) {
if (newPattern == null) return;
// Make spaces non-breaking
_pattern = newPattern.replaceAll(' ', '\u00a0');
var parser = new _NumberFormatParser(this, newPattern);
parser.parse();
}
String toString() => "NumberFormat($_locale, $_pattern)";
}
/**
* Private class that parses the numeric formatting pattern and sets the
* variables in [format] to appropriate values. Instances of this are
* transient and store parsing state in instance variables, so can only be used
* to parse a single pattern.
*/
class _NumberFormatParser {
/**
* The special characters in the pattern language. All others are treated
* as literals.
*/
static const _PATTERN_SEPARATOR = ';';
static const _QUOTE = "'";
static const _PATTERN_DIGIT = '#';
static const _PATTERN_ZERO_DIGIT = '0';
static const _PATTERN_GROUPING_SEPARATOR = ',';
static const _PATTERN_DECIMAL_SEPARATOR = '.';
static const _PATTERN_CURRENCY_SIGN = '\u00A4';
static const _PATTERN_PER_MILLE = '\u2030';
static const _PATTERN_PERCENT = '%';
static const _PATTERN_EXPONENT = 'E';
static const _PATTERN_PLUS = '+';
/** The format whose state we are setting. */
final NumberFormat format;
/** The pattern we are parsing. */
final _StringIterator pattern;
/**
* Create a new [_NumberFormatParser] for a particular [NumberFormat] and
* [input] pattern.
*/
_NumberFormatParser(this.format, input) : pattern = _iterator(input) {
pattern.moveNext();
}
/** The [NumberSymbols] for the locale in which our [format] prints. */
NumberSymbols get symbols => format.symbols;
/** Parse the input pattern and set the values. */
void parse() {
format._positivePrefix = _parseAffix();
var trunk = _parseTrunk();
format._positiveSuffix = _parseAffix();
// If we have separate positive and negative patterns, now parse the
// the negative version.
if (pattern.current == _NumberFormatParser._PATTERN_SEPARATOR) {
pattern.moveNext();
format._negativePrefix = _parseAffix();
// Skip over the negative trunk, verifying that it's identical to the
// positive trunk.
for (var each in _iterable(trunk)) {
if (pattern.current != each && pattern.current != null) {
throw new FormatException(
"Positive and negative trunks must be the same");
}
pattern.moveNext();
}
format._negativeSuffix = _parseAffix();
} else {
// If no negative affix is specified, they share the same positive affix.
format._negativePrefix = format._positivePrefix + format._negativePrefix;
format._negativeSuffix = format._negativeSuffix + format._positiveSuffix;
}
}
/** Variable used in parsing prefixes and suffixes to keep track of
* whether or not we are in a quoted region. */
bool inQuote = false;
/**
* Parse a prefix or suffix and return the prefix/suffix string. Note that
* this also may modify the state of [format].
*/
String _parseAffix() {
var affix = new StringBuffer();
inQuote = false;
var loop = true;
while (loop) {
loop = parseCharacterAffix(affix) && pattern.moveNext();
}
return affix.toString();
}
/**
* Parse an individual character as part of a prefix or suffix. Return true
* if we should continue to look for more affix characters, and false if
* we have reached the end.
*/
bool parseCharacterAffix(StringBuffer affix) {
var ch = pattern.current;
if (ch == null) return false;
if (ch == _QUOTE) {
var nextChar = pattern.peek;
if (nextChar == _QUOTE) {
pattern.moveNext();
affix.write(_QUOTE); // 'don''t'
} else {
inQuote = !inQuote;
}
return true;
}
if (inQuote) {
affix.write(ch);
} else {
switch (ch) {
case _PATTERN_DIGIT:
case _PATTERN_ZERO_DIGIT:
case _PATTERN_GROUPING_SEPARATOR:
case _PATTERN_DECIMAL_SEPARATOR:
case _PATTERN_SEPARATOR:
return false;
case _PATTERN_CURRENCY_SIGN:
// TODO(alanknight): Handle the local/global/portable currency signs
affix.write(symbols.DEF_CURRENCY_CODE);
break;
case _PATTERN_PERCENT:
if (format._multiplier != 1) {
throw new FormatException('Too many percent/permill');
}
format._multiplier = 100;
affix.write(symbols.PERCENT);
break;
case _PATTERN_PER_MILLE:
if (format._multiplier != 1) {
throw new FormatException('Too many percent/permill');
}
format._multiplier = 1000;
affix.write(symbols.PERMILL);
break;
default:
affix.write(ch);
}
}
return true;
}
/** Variables used in [_parseTrunk] and [parseTrunkCharacter]. */
var decimalPos;
var digitLeftCount;
var zeroDigitCount;
var digitRightCount;
var groupingCount;
var trunk;
/**
* Parse the "trunk" portion of the pattern, the piece that doesn't include
* positive or negative prefixes or suffixes.
*/
String _parseTrunk() {
decimalPos = -1;
digitLeftCount = 0;
zeroDigitCount = 0;
digitRightCount = 0;
groupingCount = -1;
var loop = true;
trunk = new StringBuffer();
while (pattern.current != null && loop) {
loop = parseTrunkCharacter();
}
if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {
// Handle '###.###' and '###.' and '.###'
var n = decimalPos;
if (n == 0) { // Handle '.###'
n++;
}
digitRightCount = digitLeftCount - n;
digitLeftCount = n - 1;
zeroDigitCount = 1;
}
// Do syntax checking on the digits.
if (decimalPos < 0 && digitRightCount > 0 ||
decimalPos >= 0 && (decimalPos < digitLeftCount ||
decimalPos > digitLeftCount + zeroDigitCount) ||
groupingCount == 0) {
throw new FormatException('Malformed pattern "${pattern.input}"');
}
var totalDigits = digitLeftCount + zeroDigitCount + digitRightCount;
format.maximumFractionDigits =
decimalPos >= 0 ? totalDigits - decimalPos : 0;
if (decimalPos >= 0) {
format.minimumFractionDigits =
digitLeftCount + zeroDigitCount - decimalPos;
if (format.minimumFractionDigits < 0) {
format.minimumFractionDigits = 0;
}
}
// The effectiveDecimalPos is the position the decimal is at or would be at
// if there is no decimal. Note that if decimalPos<0, then digitTotalCount
// == digitLeftCount + zeroDigitCount.
var effectiveDecimalPos = decimalPos >= 0 ? decimalPos : totalDigits;
format.minimumIntegerDigits = effectiveDecimalPos - digitLeftCount;
if (format._useExponentialNotation) {
format.maximumIntegerDigits =
digitLeftCount + format.minimumIntegerDigits;
// In exponential display, we need to at least show something.
if (format.maximumFractionDigits == 0 &&
format.minimumIntegerDigits == 0) {
format.minimumIntegerDigits = 1;
}
}
format._groupingSize = max(0, groupingCount);
format._decimalSeparatorAlwaysShown = decimalPos == 0 ||
decimalPos == totalDigits;
return trunk.toString();
}
/**
* Parse an individual character of the trunk. Return true if we should
* continue to look for additional trunk characters or false if we have
* reached the end.
*/
bool parseTrunkCharacter() {
var ch = pattern.current;
switch (ch) {
case _PATTERN_DIGIT:
if (zeroDigitCount > 0) {
digitRightCount++;
} else {
digitLeftCount++;
}
if (groupingCount >= 0 && decimalPos < 0) {
groupingCount++;
}
break;
case _PATTERN_ZERO_DIGIT:
if (digitRightCount > 0) {
throw new FormatException('Unexpected "0" in pattern "'
+ pattern.input + '"');
}
zeroDigitCount++;
if (groupingCount >= 0 && decimalPos < 0) {
groupingCount++;
}
break;
case _PATTERN_GROUPING_SEPARATOR:
groupingCount = 0;
break;
case _PATTERN_DECIMAL_SEPARATOR:
if (decimalPos >= 0) {
throw new FormatException(
'Multiple decimal separators in pattern "$pattern"');
}
decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
break;
case _PATTERN_EXPONENT:
trunk.write(ch);
if (format._useExponentialNotation) {
throw new FormatException(
'Multiple exponential symbols in pattern "$pattern"');
}
format._useExponentialNotation = true;
format.minimumExponentDigits = 0;
// exponent pattern can have a optional '+'.
pattern.moveNext();
var nextChar = pattern.current;
if (nextChar == _PATTERN_PLUS) {
trunk.write(pattern.current);
pattern.moveNext();
format._useSignForPositiveExponent = true;
}
// Use lookahead to parse out the exponential part
// of the pattern, then jump into phase 2.
while (pattern.current == _PATTERN_ZERO_DIGIT) {
trunk.write(pattern.current);
pattern.moveNext();
format.minimumExponentDigits++;
}
if ((digitLeftCount + zeroDigitCount) < 1 ||
format.minimumExponentDigits < 1) {
throw new FormatException(
'Malformed exponential pattern "$pattern"');
}
return false;
default:
return false;
}
trunk.write(ch);
pattern.moveNext();
return true;
}
}
/**
* Returns an [Iterable] on the string as a list of substrings.
*/
Iterable _iterable(String s) => new _StringIterable(s);
/**
* Return an iterator on the string as a list of substrings.
*/
Iterator _iterator(String s) => new _StringIterator(s);
// TODO(nweiz): remove this when issue 3780 is fixed.
/**
* Provides an Iterable that wraps [_iterator] so it can be used in a `for`
* loop.
*/
class _StringIterable extends IterableBase<String> {
final Iterator<String> iterator;
_StringIterable(String s)
: iterator = _iterator(s);
}
/**
* Provides an iterator over a string as a list of substrings, and also
* gives us a lookahead of one via the [peek] method.
*/
class _StringIterator implements Iterator<String> {
String input;
var index = -1;
inBounds(i) => i >= 0 && i < input.length;
_StringIterator(this.input);
String get current => inBounds(index) ? input[index] : null;
bool moveNext() => inBounds(++index);
String get peek => inBounds(index + 1) ? input[index + 1] : null;
Iterator<String> get iterator => this;
}
part of angular.mock;
class _MockXhr {
var $$method, $$url, $$async, $$reqHeaders, $$respHeaders;
open(method, url, async) {
$$method = method;
$$url = url;
$$async = async;
$$reqHeaders = {};
$$respHeaders = {};
}
var $$data;
send(data) {
$$data = data;
}
setRequestHeader(key, value) {
$$reqHeaders[key] = value;
}
getResponseHeader(name) {
// the lookup must be case insensitive, that's why we try two quick lookups and full scan at last
if ($$respHeaders.containsKey(name)) {
return $$respHeaders[name];
}
name = name.toLowerCase();
if ($$respHeaders.containsKey(name)) {
return $$respHeaders[name];
}
String header = null;
$$respHeaders.forEach((headerName, headerVal) {
if (header != null) return;
if (headerName.toLowerCase()) header = headerVal;
});
return header;
}
getAllResponseHeaders() {
if ($$respHeaders == null) return '';
var lines = [];
$$respHeaders.forEach((key, value) {
lines.add("$key: $value");
});
return lines.join('\n');
}
// noop
abort() {}
}
/**
* An internal class used by [MockHttpBackend].
*/
class MockHttpExpectation {
var method, url, data, headers;
var response;
MockHttpExpectation(this.method, this.url, [this.data, this.headers]);
match(m, u, [d, h]) {
if (method != m) return false;
if (!matchUrl(u)) return false;
if (d != null && !matchData(d)) return false;
if (h != null && !matchHeaders(h)) return false;
return true;
}
matchUrl(u) {
if (url == null) return true;
if (url is RegExp) return url.hasMatch(u);
return url == u;
}
matchHeaders(h) {
if (headers == null) return true;
if (headers is Function) return headers(h);
return "$headers" == "$h";
}
matchData(d) {
if (data == null) return true;
if (d == null) return false; // data is not null, but d is.
if (data is File) return data == d;
assert(d is String);
if (data is RegExp) return data.hasMatch(d);
return json.stringify(data) == json.stringify(d);
}
toString() {
return "$method $url";
}
}
class _Chain {
var _respondFn;
_Chain({respond}) {
_respondFn = respond;
}
respond([x,y,z]) => _respondFn(x,y,z);
}
/**
* A mock implementation of [HttpBackend], used in tests.
*/
class MockHttpBackend implements HttpBackend {
var definitions = [],
expectations = [],
responses = [];
/**
* This function is called from [Http] and designed to mimic the Dart APIs.
*/
Future request(String url,
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
Completer c = new Completer();
var callback = (status, data, headers) {
if (status >= 200 && status < 300) {
c.complete(new MockHttpRequest(status, data, headers));
} else {
c.completeError(
new MockProgressEvent(
new MockHttpRequest(status, data, headers)));
}
};
call(method == null ? 'GET' : method, url, sendData, callback, requestHeaders);
return c.future;
}
_createResponse(status, data, headers) {
if (status is Function) return status;
return ([a,b,c,d,e]) {
return status is num
? [status, data, headers]
: [200, status, data];
};
}
/**
* A callback oriented API. This function takes a callback with
* will be called with (status, data, headers)
*/
call(method, [url, data, callback, headers, timeout]) {
var xhr = new _MockXhr(),
expectation = expectations.isEmpty ? null : expectations[0],
wasExpected = false;
var prettyPrint = (data) {
return (data is String || data is Function || data is RegExp)
? data
: json.stringify(data);
};
var wrapResponse = (wrapped) {
var handleResponse = () {
var response = wrapped.response(method, url, data, headers);
xhr.$$respHeaders = response[2];
utils.relaxFnApply(callback, [response[0], response[1], xhr.getAllResponseHeaders()]);
};
var handleTimeout = () {
for (var i = 0, ii = responses.length; i < ii; i++) {
if (identical(responses[i], handleResponse)) {
responses.removeAt(i);
callback(-1, null, '');
break;
}
}
};
if (timeout != null) timeout.then(handleTimeout);
return handleResponse;
};
if (expectation != null && expectation.match(method, url)) {
if (!expectation.matchData(data))
throw ['Expected $expectation with different data\n' +
'EXPECTED: ${prettyPrint(expectation.data)}\nGOT: $data'];
if (!expectation.matchHeaders(headers))
throw ['Expected $expectation with different headers\n' +
'EXPECTED: ${prettyPrint(expectation.headers)}\nGOT: ${prettyPrint(headers)}'];
expectations.removeAt(0);
if (expectation.response != null) {
responses.add(wrapResponse(expectation));
return;
}
wasExpected = true;
}
for (var definition in definitions) {
if (definition.match(method, url, data, headers != null ? headers : {})) {
if (definition.response != null) {
// if $browser specified, we do auto flush all requests
responses.add(wrapResponse(definition));
} else throw ['No response defined !'];
return;
}
}
throw wasExpected ?
['No response defined !'] :
['Unexpected request: $method $url\n' +
(expectation != null ? 'Expected $expectation' : 'No more requests expected')];
}
/**
* Creates a new backend definition.
*
* @param {string} method HTTP method.
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current definition.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*
* - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string) and response headers
* (Object).
*/
when(method, [url, data, headers]) {
var definition = new MockHttpExpectation(method, url, data, headers),
chain = new _Chain(respond: (status, data, headers) {
definition.response = _createResponse(status, data, headers);
});
definitions.add(definition);
return chain;
}
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenGET
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for GET requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
whenGET(url, [headers]) =>
when('GET', url, null, headers);
whenDELETE(url, [headers]) =>
when('DELETE', url, null, headers);
whenJSONP(url, [headers]) =>
when('JSONP', url, null, headers);
whenPUT(url, [data, headers]) =>
when('PUT', url, data, headers);
whenPOST(url, [data, headers]) =>
when('POST', url, data, headers);
whenPATCH(url, [data, headers]) =>
when('PATCH', url, data, headers);
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenHEAD
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for HEAD requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenDELETE
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for DELETE requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenPOST
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for POST requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenPUT
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for PUT requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#whenJSONP
* @methodOf ngMock.$httpBackend
* @description
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
//createShortMethods('when');
/**
* @ngdoc method
* @name ngMock.$httpBackend#expect
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation.
*
* @param {string} method HTTP method.
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current expectation.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*
* - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}`
* – The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string) and response headers
* (Object).
*/
expect(method, [url, data, headers]) {
var expectation = new MockHttpExpectation(method, url, data, headers);
expectations.add(expectation);
return new _Chain(respond: (status, data, headers) {
expectation.response = _createResponse(status, data, headers);
});
}
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectGET
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for GET requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled. See #expect for more info.
*/
expectGET(url, [headers]) =>
expect('GET', url, null, headers);
expectDELETE(url, [headers]) =>
expect('DELETE', url, null, headers);
expectJSONP(url, [headers]) =>
expect('JSONP', url, null, headers);
expectPUT(url, [data, headers]) =>
expect('PUT', url, data, headers);
expectPOST(url, [data, headers]) =>
expect('POST', url, data, headers);
expectPATCH(url, [data, headers]) =>
expect('PATCH', url, data, headers);
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectHEAD
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for HEAD requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectDELETE
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for DELETE requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectPOST
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for POST requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectPUT
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for PUT requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectPATCH
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for PATCH requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp)=} data HTTP request body.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name ngMock.$httpBackend#expectJSONP
* @methodOf ngMock.$httpBackend
* @description
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* request is handled.
*/
//createShortMethods('expect');
/**
* @ngdoc method
* @name ngMock.$httpBackend#flush
* @methodOf ngMock.$httpBackend
* @description
* Flushes all pending requests using the trained responses.
*
* @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
* all pending requests will be flushed. If there are no pending requests when the flush method
* is called an exception is thrown (as this typically a sign of programming error).
*/
flush([count]) {
if (responses.isEmpty) throw ['No pending request to flush !'];
if (count != null) {
while (count-- > 0) {
if (responses.isEmpty) throw ['No more pending request to flush !'];
responses.removeAt(0)();
}
} else {
while (!responses.isEmpty) {
responses.removeAt(0)();
}
}
verifyNoOutstandingExpectation();
}
/**
* @ngdoc method
* @name ngMock.$httpBackend#verifyNoOutstandingExpectation
* @methodOf ngMock.$httpBackend
* @description
* Verifies that all of the requests defined via the `expect` api were made. If any of the
* requests were not made, verifyNoOutstandingExpectation throws an exception.
*
* Typically, you would call this method following each test case that asserts requests using an
* "afterEach" clause.
*
* <pre>
* afterEach($httpBackend.verifyNoOutstandingExpectation);
* </pre>
*/
verifyNoOutstandingExpectation() {
if (!expectations.isEmpty) {
throw ['Unsatisfied requests: ${expectations.join(', ')}'];
}
}
/**
* @ngdoc method
* @name ngMock.$httpBackend#verifyNoOutstandingRequest
* @methodOf ngMock.$httpBackend
* @description
* Verifies that there are no outstanding requests that need to be flushed.
*
* Typically, you would call this method following each test case that asserts requests using an
* "afterEach" clause.
*
* <pre>
* afterEach($httpBackend.verifyNoOutstandingRequest);
* </pre>
*/
verifyNoOutstandingRequest() {
if (!responses.isEmpty) {
throw ['Unflushed requests: ${responses.length}'];
}
}
/**
* @ngdoc method
* @name ngMock.$httpBackend#resetExpectations
* @methodOf ngMock.$httpBackend
* @description
* Resets all request expectations, but preserves all backend definitions. Typically, you would
* call resetExpectations during a multiple-phase test when you want to reuse the same instance of
* $httpBackend mock.
*/
resetExpectations() {
expectations.length = 0;
responses.length = 0;
}
}
/**
* Mock implementation of the [HttpRequest] object returned from the HttpBackend.
*/
class MockHttpRequest implements HttpRequest {
final bool supportsCrossOrigin = false;
final bool supportsLoadEndEvent = false;
final bool supportsOverrideMimeType = false;
final bool supportsProgressEvent = false;
final Events on = null;
final Stream<ProgressEvent> onAbort = null;
final Stream<ProgressEvent> onError = null;
final Stream<ProgressEvent> onLoad = null;
final Stream<ProgressEvent> onLoadEnd = null;
final Stream<ProgressEvent> onLoadStart = null;
final Stream<ProgressEvent> onProgress = null;
final Stream<ProgressEvent> onReadyStateChange = null;
final Stream<ProgressEvent> onTimeout = null;
final int readyState = 0;
get responseText => response == null ? null : "$response";
final responseXml = null;
final String statusText = null;
final HttpRequestUpload upload = null;
String responseType = null;
int timeout = 0;
bool withCredentials;
final int status;
final response;
final String headers;
MockHttpRequest(int this.status, String this.response, [this.headers]);
void abort() {}
bool dispatchEvent(Event event) => false;
String getAllResponseHeaders() {
if (headers == null) return null;
return headers;
}
String getResponseHeader(String header) => null;
void open(String method, String url, {bool async, String user, String password}) {}
void overrideMimeType(String override) {}
void send([data]) {}
void setRequestHeader(String header, String value) {}
void $dom_addEventListener(String type, EventListener listener, [bool useCapture]) {}
void $dom_removeEventListener(String type, EventListener listener, [bool useCapture]) {}
}
class MockProgressEvent implements ProgressEvent {
final bool bubbles = false;
final bool cancelable = false;
final DataTransfer clipboardData = null;
final EventTarget currentTarget;
final bool defaultPrevented = false;
final int eventPhase = 0;
final bool lengthComputable = false;
final int loaded = 0;
final List<Node> path = null;
final int position = 0;
final Type runtimeType = null;
final EventTarget target = null;
final int timeStamp = 0;
final int total = 0;
final int totalSize = 0;
final String type = null;
bool cancelBubble = false;
MockProgressEvent(MockHttpRequest this.currentTarget);
void preventDefault() {}
void stopImmediatePropagation() {}
void stopPropagation() {}
}
library angular.io_impl;
import 'dart:io';
import 'io.dart';
class IoServiceImpl implements IoService {
String readAsStringSync(String filePath) =>
new File(filePath).readAsStringSync();
void visitFs(String rootDir, FsVisitor visitor) {
Directory root = new Directory(rootDir);
if (!FileSystemEntity.isDirectorySync(rootDir)) {
throw 'Expected $rootDir to be a directory!';
}
root.listSync(recursive: true, followLinks: true).forEach((entity) {
if (entity.statSync().type == FileSystemEntityType.FILE) {
visitor(entity.path);
}
});
}
}
part of angular.directive;
/**
* The ngBind attribute tells Angular to replace the text content of the
* specified HTML element with the value of a given expression, and to update
* the text content when the value of that expression changes.
*
* Typically, you don't use ngBind directly, but instead you use the double
* curly markup like {{ expression }} which is similar but less verbose.
*
* It is preferrable to use ngBind instead of {{ expression }} when a template
* is momentarily displayed by the browser in its raw state before Angular
* compiles it. Since ngBind is an element attribute, it makes the bindings
* invisible to the user while the page is loading.
*
* An alternative solution to this problem would be using the ngCloak directive.
*/
@NgDirective(
selector: '[ng-bind]',
map: const {'ng-bind': '=.value'})
class NgBindDirective {
dom.Element element;
NgBindDirective(dom.Element this.element);
set value(value) => element.text = value == null ? '' : value.toString();
}
library angular;
export 'bootstrap.dart';
export 'core/module.dart';
export 'core_dom/module.dart';
export 'core/parser/parser_library.dart';
export 'directive/module.dart';
export 'filter/module.dart';
part of angular.core;
typedef void ZoneOnTurn();
typedef void ZoneOnError(dynamic error, dynamic stacktrace, LongStackTrace longStacktrace);
/**
* Contains the locations of runAsync calls across VM turns.
*/
class LongStackTrace {
final String reason;
final dynamic stacktrace;
final LongStackTrace parent;
LongStackTrace(this.reason, this.stacktrace, this.parent);
toString() {
List<String> frames = '${this.stacktrace}'.split('\n');
frames = frames.where((frame) {
return frame.indexOf('(dart:') == -1 && // skip dart runtime libs
frame.indexOf('(package:angular/zone.dart') == -1; // skip angular zone
}).toList();
frames.insert(0, reason);
var parent = this.parent == null ? '' : this.parent;
return '${frames.join("\n ")}\n$parent';
}
}
/**
* A better zone API which implements onTurnDone.
*/
class Zone {
Zone() {
_zone = async.Zone.current.fork(specification: new async.ZoneSpecification(
run: _onRun,
runUnary: _onRunUnary,
scheduleMicrotask: _onScheduleMicrotask,
handleUncaughtError: _uncaughtError
));
}
async.Zone _zone;
List _asyncQueue = [];
bool _errorThrownFromOnRun = false;
_onRunBase(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
_runningInTurn++;
try {
return fn();
} catch (e, s) {
onError(e, s, _longStacktrace);
_errorThrownFromOnRun = true;
rethrow;
} finally {
_runningInTurn--;
if (_runningInTurn == 0)
_finishTurn(zone, delegate);
}
}
// Called from the parent zone.
_onRun(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
return _onRunBase(self, delegate, zone, () => delegate.run(zone, fn));
}
_onRunUnary(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn(args), args) {
return _onRunBase(self, delegate, zone, () => delegate.runUnary(zone, fn, args));
}
_onScheduleMicrotask(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, fn()) {
_asyncQueue.add(() => delegate.run(zone, fn));
if (_runningInTurn == 0 && !_inFinishTurn) {
_finishTurn(zone, delegate);
}
}
_uncaughtError(async.Zone self, async.ZoneDelegate delegate, async.Zone zone, e) {
if (!_errorThrownFromOnRun) {
onError(e, async.getAttachedStackTrace(e), _longStacktrace);
}
_errorThrownFromOnRun = false;
}
var _inFinishTurn = false;
_finishTurn(zone, delegate) {
if (_inFinishTurn) {
return;
}
_inFinishTurn = true;
try {
// Two loops here: the inner one runs all queued microtasks,
// the outer runs onTurnDone (e.g. scope.$digest) and then
// any microtasks which may have been queued from onTurnDone.
do {
while (!_asyncQueue.isEmpty) {
delegate.run(zone, _asyncQueue.removeAt(0));
}
delegate.run(zone, onTurnDone);
} while (!_asyncQueue.isEmpty);
} catch (e, s) {
onError(e, s, _longStacktrace);
_errorThrownFromOnRun = true;
rethrow;
} finally {
_inFinishTurn = false;
}
}
int _runningInTurn = 0;
/**
* A function called with any errors from the zone.
*/
var onError = (e, s, ls) => null;
/**
* A function that is called at the end of each VM turn in which the
* in-zone code or any runAsync callbacks were run.
*/
var onTurnDone = () => null; // Type was ZoneOnTurn: dartbug 13519
/**
* A function that is called when uncaught errors are thrown inside the zone.
*/
// var onError = (dynamic e, dynamic s, LongStackTrace ls) => print('EXCEPTION: $e\n$s\n$ls');
// Type was ZoneOnError: dartbug 13519
LongStackTrace _longStacktrace = null;
LongStackTrace _getLongStacktrace(name) {
var shortStacktrace = 'Long-stacktraces supressed in production.';
assert((shortStacktrace = _getStacktrace()) != null);
return new LongStackTrace(name, shortStacktrace, _longStacktrace);
}
_getStacktrace() {
try { throw []; } catch (e, s) {
return s;
}
}
/**
* Runs the provided function in the zone. Any runAsync calls (e.g. futures)
* will also be run in this zone.
*
* Returns the return value of body.
*/
run(body()) {
return _zone.run(body);
}
assertInTurn() {
assert(_runningInTurn > 0 || _inFinishTurn);
}
assertInZone() {
assertInTurn();
}
}
part of angular.filter;
// Too bad you can't stick typedef's inside a class.
typedef bool Predicate(e);
typedef bool Equals(a, b);
/**
* Selects a subset of items from the provided [List] and returns it as a new
* [List].
*
* In addition to the input list (implicit in an Angular expression syntax),
* this filter takes 1 required and 1 optional parameter. They are:
*
* - `expression` (required) - one of [Map], [Function], [String], [bool], [num]
* - `comparator` (optional)
*
* <br>
*
* # expression
*
* can be one of:
*
* - [String], [bool] and [num]: Only items in the List that directly
* match this expression, items that are Maps with any value matching this
* item and items that are Lists containing a matching items are returned.
*
* - [Map]: This defines a pattern map. Filters specific properties on objects contained
* in the input List. For example `{name:"M", phone:"1"}` predicate will
* return a list of items which have property `name` containing "M" and
* property `phone` containing "1". A special property name, `$`, can be used
* (as in `{$: "text"}`) to accept a match against any property of the object.
* That's equivalent to the simple substring match with a `String` as
* described above.
*
* - [Function]: This allows you to supply a custom function to filter the
* List. The function is called for each element of the List. The returned
* List contains exactly those elements for which this function returned
* `true`.
*
* <br>
*
* # comparator
*
* can be one of:
*
* - `bool comparator(expected, actual)`: The function will be called with the
* object value and the predicate value to compare and should return true if the
* item should be included in filtered result.
*
* - `true`: Specifies that only identical objects matching the expression
* exactly should be considered matches. Two strings are considered identical
* if they are equal. Two numbers are considered identical if they are either
* equal or both are `NaN`. All other objects are identical iff
* identical(expected, actual) is true.
*
* - `false|null`: Specifies case insensitive substring matching.
*
* <br>
*
* # Example:
*
* // main.dart
* import 'package:angular/angular.dart';
*
* @NgDirective(selector: '[toy-data]')
* class ToyData {
* ToyData(Scope scope) {
* scope.friends = [{name:'John', phone:'555-1276'},
* {name:'Mary', phone:'800-BIG-MARY'},
* {name:'Mike', phone:'555-4321'},
* {name:'Adam', phone:'555-5678'},
* {name:'Julie', phone:'555-8765'},
* {name:'Juliette', phone:'555-5678'}];
* }
* }
*
* main() {
* bootstrapAngular([new AngularModule()..type(ToyData)], 'html');
* }
*
* and
*
* <!-- index.html -->
* <html>
* <head>
* <script src="packages/browser/dart.js"></script>
* <script src="main.dart" type="application/dart"></script>
* </head>
* <body toy-data>
* Search: <input ng-model="searchText">
* <table id="searchTextResults">
* <tr><th>Name</th><th>Phone</th></tr>
* <tr ng-repeat="friend in friends | filter:searchText">
* <td>{{friend.name}}</td>
* <td>{{friend.phone}}</td>
* </tr>
* </table>
* <hr>
* Any: <input ng-model="search.$"> <br>
* Name only <input ng-model="search.name"><br>
* Phone only <input ng-model="search.phone"><br>
* Equality <input type="checkbox" ng-model="strict"><br>
* <table id="searchObjResults">
* <tr><th>Name</th><th>Phone</th></tr>
* <tr ng-repeat="friend in friends | filter:search:strict">
* <td>{{friend.name}}</td>
* <td>{{friend.phone}}</td>
* </tr>
* </table>
* </body>
* </html>
*/
@NgFilter(name: 'filter')
class FilterFilter {
Parser _parser;
Equals _comparator;
FilterFilter(Parser this._parser);
static _nop(e) => e;
static _ensureBool(val) => (val is bool && val);
static _isSubstringCaseInsensitive(String a, String b) =>
a != null && b != null && a.toLowerCase().contains(b.toLowerCase());
static _identical(a, b) => identical(a, b) ||
(a is String && b is String && a == b) ||
(a is num && b is num && a.isNaN && b.isNaN);
// Used by _defaultComparator to compare strings.
Equals _stringComparator;
Equals _configureComparator(var comparatorExpression) {
if (comparatorExpression == null || comparatorExpression == false) {
_stringComparator = _isSubstringCaseInsensitive;
_comparator = _defaultComparator;
} else if (comparatorExpression == true) {
_stringComparator = _identical;
_comparator = _defaultComparator;
} else if (comparatorExpression is Equals) {
_comparator = (a, b) => _ensureBool(comparatorExpression(a, b));
} else {
_comparator = null;
}
}
// Preconditions
// - what: NOT a Map
// - item: neither a Map nor a List
bool _defaultComparator(var item, var what) {
if (what == null) {
return false;
} else if (item == null) {
return what == '';
}
if (what is String && what.startsWith('!')) {
return !_search(item, what.substring(1));
}
if (item is String) {
return (what is String) && _stringComparator(item, what);
} else if (item is bool) {
if (what is bool) {
return item == what;
} else if (what is String) {
what = (what as String).toLowerCase();
// TODO(chirayu): Support this extension? Yes => add tests.
return item ? (what == "true" || what == "yes" || what == "on")
: (what == "false" || what == "no" || what == "off");
} else {
return false;
}
} else if (item is num) {
if (what is num) {
return item == what || (item.isNaN && what.isNaN);
} else {
// TODO(chirayu): I think this expression is more appropriate here.
//
// return what is String && '$item' == what;
return what is String && _stringComparator('$item', what);
}
} else {
return false; // Unsupported item type.
}
}
bool _search(var item, var what) {
if (what is Map) {
return what.keys.every((key) {
return _search((key == r'$') ? item : _parser(key).eval(item, null),
what[key]);
});
} else {
if (item is Map) {
return item.keys.any((k) => !k.startsWith(r'$') && _search(item[k], what));
} else if (item is List) {
return item.any((i) => _search(i, what));
} else {
return _comparator(item, what);
}
}
}
Predicate _toPredicate(var expression) {
if (expression is Predicate) {
return (item) => _ensureBool(expression(item));
} else if (_comparator == null) {
return (item) => false; // Bad comparator → no items for you!
} else {
return (item) => _search(item, expression);
}
}
List call(List items, var expression, [var comparator]) {
if (expression == null) {
return items.toList(); // Missing expression → passthrough.
} else if (expression is! Map && expression is! Function &&
expression is! String && expression is! bool && expression is! num) {
return const []; // Bad expression → no items for you!
}
_configureComparator(comparator);
// TODO(chirayu): Important to set _comparator=null before return?
return items.where(_toPredicate(expression)).toList();
}
}
library angular.core;
import 'dart:async' as async;
import 'dart:collection';
import 'dart:json';
import 'dart:mirrors';
import 'package:di/di.dart';
import 'package:perf_api/perf_api.dart';
import 'package:meta/meta.dart';
import 'parser/parser_library.dart';
import '../utils.dart';
part "cache.dart";
part "directive.dart";
part "exception_handler.dart";
part "filter.dart";
part "interpolate.dart";
part "registry.dart";
part "scope.dart";
part "zone.dart";
class NgCoreModule extends Module {
NgCoreModule() {
type(ScopeDigestTTL);
type(MetadataExtractor);
type(Cache);
type(DirectiveMap);
type(ExceptionHandler);
type(FilterMap);
type(Interpolate);
type(Scope);
type(Zone);
type(Parser, implementedBy: DynamicParser);
type(DynamicParser);
type(Lexer);
type(ParserBackend);
type(GetterSetter);
}
}
library angular.bootstrap;
import 'dart:html' as dom;
import 'package:di/di.dart';
import 'package:di/dynamic_injector.dart';
import 'package:perf_api/perf_api.dart';
import 'core/module.dart';
import 'core_dom/module.dart';
import 'directive/module.dart';
import 'filter/module.dart';
import 'perf/module.dart';
/**
* This is the top level module which describes the whole of angular.
*
* The Module is made up or
*
* - [NgCoreModule]
* - [NgCoreDomModule]
* - [NgFilterModule]
* - [NgPerfModule]
*/
class AngularModule extends Module {
AngularModule() {
install(new NgCoreModule());
install(new NgCoreDomModule());
install(new NgDirectiveModule());
install(new NgFilterModule());
install(new NgPerfModule());
}
}
Injector _defaultInjectorFactory(List<Module> modules) =>
new DynamicInjector(modules: modules);
// helper for bootstrapping angular
bootstrapAngular(modules, [rootElementSelector = '[ng-app]',
Injector injectorFactory(List<Module> modules) = _defaultInjectorFactory]) {
var allModules = new List.from(modules);
List<dom.Node> topElt = dom.query(rootElementSelector).nodes.toList();
assert(topElt.length > 0);
// The injector must be created inside the zone, so we create the
// zone manually and give it back to the injector as a value.
Zone zone = new Zone();
allModules.add(new Module()..value(Zone, zone));
return zone.run(() {
Injector injector = injectorFactory(allModules);
injector.get(Compiler)(topElt)(injector, topElt);
return injector;
});
}
part of angular.filter;
/**
* Formats a number as a currency (ie $1,234.56). When no currency symbol is
* provided, '$' used.
*
*
* Usage:
*
* {{ number_expression | number[:fractionSize] }}
*
*/
@NgFilter(name:'currency')
class CurrencyFilter {
NumberFormat nf = new NumberFormat();
CurrencyFilter() {
nf.minimumFractionDigits = 2;
nf.maximumFractionDigits = 2;
}
/**
* [value]: the value to format
*
* [symbol]: Symbol to use.
*
* [leading]: Symbol should be placed in front of the number
*/
call(value, [symbol = r'$', leading = true]) {
if (value is String) value = double.parse(value);
if (!(value is num)) return value;
if (value.isNaN) return '';
var neg = value < 0;
if (neg) value = -value;
var before = neg ? '(' : '';
var after = neg ? ')' : '';
if (leading) {
return '$before$symbol${nf.format(value)}$after';
} else {
return '$before${nf.format(value)}$symbol$after';
}
}
}
part of angular.directive;
class _Row {
var id;
Scope scope;
Block block;
dom.Element startNode;
dom.Element endNode;
List<dom.Element> elements;
_Row(this.id);
}
/**
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
* instance gets its own scope, where the given loop variable is set to the current collection item,
* and `$index` is set to the item index or key.
*
* Special properties are exposed on the local scope of each template instance, including:
*
* <table>
* <tr><th> Variable </th><th> Type </th><th> Details <th></tr>
* <tr><td> `$index` </td><td>[num] </td><td> iterator offset of the repeated element (0..length-1) <td></tr>
* <tr><td> `$first` </td><td>[bool]</td><td> true if the repeated element is first in the iterator. <td></tr>
* <tr><td> `$middle` </td><td>[bool]</td><td> true if the repeated element is between the first and last in the iterator. <td></tr>
* <tr><td> `$last` </td><td>[bool]</td><td> true if the repeated element is last in the iterator. <td></tr>
* <tr><td> `$even` </td><td>[bool]</td><td> true if the iterator position `$index` is even (otherwise false). <td></tr>
* <tr><td> `$odd` </td><td>[bool]</td><td> true if the iterator position `$index` is odd (otherwise false). <td></tr>
* </table>
*
*
* [repeat_expression] ngRepeat The expression indicating how to enumerate a collection. These
* formats are currently supported:
*
* * `variable in expression` – where variable is the user defined loop variable and `expression`
* is a scope expression giving the collection to enumerate.
*
* For example: `album in artist.albums`.
*
* * `variable in expression track by tracking_expression` – You can also provide an optional
* tracking function which can be used to associate the objects in the collection with the DOM
* elements. If no tracking function is specified the ng-repeat associates elements by identity
* in the collection. It is an error to have more than one tracking function to resolve to the
* same key. (This would mean that two distinct objects are mapped to the same DOM element,
* which is not possible.) Filters should be applied to the expression, before specifying a
* tracking expression.
*
* For example: `item in items` is equivalent to `item in items track by $id(item)'. This
* implies that the DOM elements will be associated by item identity in the array.
*
* For example: `item in items track by $id(item)`. A built in `$id()` function can be used
* to assign a unique `$$hashKey` property to each item in the array. This property is then
* used as a key to associated DOM elements with the corresponding item in the array by
* identity. Moving the same object in array would move the DOM element in the same way ian the
* DOM.
*
* For example: `item in items track by item.id` is a typical pattern when the items come from
* the database. In this case the object identity does not matter. Two objects are considered
* equivalent as long as their `id` property is same.
*
* For example: `item in items | filter:searchText track by item.id` is a pattern that might be
* used to apply a filter to items in conjunction with a tracking expression.
*
*
* # Example:
*
* <ul ng-repeat="item in ['foo', 'bar', 'baz']">
* <li>{{$item}}</li>
* </ul>
*/
@NgDirective(
children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector: '[ng-repeat]',
map: const {'.': '@.expression'})
class NgRepeatDirective {
static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$');
static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$');
BlockHole _blockHole;
BoundBlockFactory _boundBlockFactory;
Scope _scope;
String _expression;
String _valueIdentifier;
String _keyIdentifier;
String _listExpr;
Map<Object, _Row> _rows = new Map<dynamic, _Row>();
Function _trackByIdFn = (key, value, index) => value;
Function _removeWatch = () => null;
NgRepeatDirective(BlockHole this._blockHole,
BoundBlockFactory this._boundBlockFactory,
Scope this._scope);
set expression(value) {
_expression = value;
_removeWatch();
Match match = _SYNTAX.firstMatch(_expression);
if (match == null) {
throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '$_expression'.";
}
_listExpr = match.group(2);
var assignExpr = match.group(1);
match = _LHS_SYNTAX.firstMatch(assignExpr);
if (match == null) {
throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '$assignExpr'.";
}
_valueIdentifier = match.group(3);
if (_valueIdentifier == null) _valueIdentifier = match.group(1);
_keyIdentifier = match.group(2);
_removeWatch = _scope.$watchCollection(_listExpr, _onCollectionChange);
}
List<_Row> _computeNewRows(collection, trackById) {
List<_Row> newRowOrder = [];
// Same as lastBlockMap but it has the current state. It will become the
// lastBlockMap on the next iteration.
Map<dynamic, _Row> newRows = new Map<dynamic, _Row>();
var arrayLength = collection.length;
// locate existing items
var length = newRowOrder.length = collection.length;
for (var index = 0; index < length; index++) {
var value = collection[index];
trackById = _trackByIdFn(index, value, index);
if (_rows.containsKey(trackById)) {
var row = _rows[trackById];
_rows.remove(trackById);
newRows[trackById] = row;
newRowOrder[index] = row;
} else if (newRows.containsKey(trackById)) {
// restore lastBlockMap
newRowOrder.forEach((row) {
if (row != null && row.startNode != null) {
_rows[row.id] = row;
}
});
// This is a duplicate and we need to throw an error
throw "[NgErr50] ngRepeat error! Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: $_expression, Duplicate key: $trackById";
} else {
// new never before seen row
newRowOrder[index] = new _Row(trackById);
newRows[trackById] = null;
}
}
// remove existing items
_rows.forEach((key, row){
row.block.remove();
row.scope.$destroy();
});
_rows = newRows;
return newRowOrder;
}
_onCollectionChange(collection) {
var previousNode = _blockHole.elements[0], // current position of the node
nextNode,
childScope,
trackById,
cursor = _blockHole;
if (collection is! List) {
collection = [];
}
List<_Row> newRowOrder = _computeNewRows(collection, trackById);
for (var index = 0, length = collection.length; index < length; index++) {
var key = index;
var value = collection[index];
_Row row = newRowOrder[index];
if (row.startNode != null) {
// if we have already seen this object, then we need to reuse the
// associated scope/element
childScope = row.scope;
nextNode = previousNode;
do {
nextNode = nextNode.nextNode;
} while(nextNode != null);
if (row.startNode == nextNode) {
// do nothing
} else {
// existing item which got moved
row.block.moveAfter(cursor);
}
previousNode = row.endNode;
} else {
// new item which we don't know about
childScope = _scope.$new();
}
childScope[_valueIdentifier] = value;
childScope[r'$index'] = index;
childScope[r'$first'] = (index == 0);
childScope[r'$last'] = (index == (collection.length - 1));
childScope[r'$middle'] = !(childScope.$first || childScope.$last);
childScope[r'$odd'] = index & 1 == 1;
childScope[r'$even'] = index & 1 == 0;
if (row.startNode == null) {
_rows[row.id] = row;
var block = _boundBlockFactory(childScope);
row.block = block;
row.scope = childScope;
row.elements = block.elements;
row.startNode = row.elements[0];
row.endNode = row.elements[row.elements.length - 1];
block.insertAfter(cursor);
}
cursor = row.block;
}
}
}
part of angular.filter;
/**
* Converts string to lowercase.
*
* Usage:
*
* {{ lowercase_expression | lowercase }}
*/
@NgFilter(name:'lowercase')
class LowercaseFilter {
call(String text) => text == null ? text : text.toLowerCase();
}
part of angular.core;
class NgAnnotation {
/**
* CSS selector which will trigger this component/directive.
* CSS Selectors are limited to a single element and can contain:
*
* * `element-name` limit to a given element name.
* * `.class` limit to an element with a given class.
* * `[attribute]` limit to an element with a given attribute name.
* * `[attribute=value]` limit to an element with a given attribute and value.
*
*
* Example: `input[type=checkbox][ng-model]`
*/
final String selector;
/**
* Specifies the compiler action to be taken on the child nodes of the
* element which this currently being compiled. The values are:
*
* * [COMPILE_CHILDREN] (*default*)
* * [TRANSCLUDE_CHILDREN]
* * [IGNORE_CHILDREN]
*/
final String children;
/**
* Compile the child nodes of the element. This is the default.
*/
static const String COMPILE_CHILDREN = 'compile';
/**
* Compile the child nodes for transclusion and makes available
* [BoundBlockFactory], [BlockFactory] and [BlockHole] for injection.
*/
static const String TRANSCLUDE_CHILDREN = 'transclude';
/**
* Do not compile/visit the child nodes. Angular markup on descendant nodes
* will not be processed.
*/
static const String IGNORE_CHILDREN = 'ignore';
/**
* A directive/component controller class can be injected into other
* directives/components. This attribute controls whether the
* controller is available to others.
*
* * `local` [NgDirective.LOCAL_VISIBILITY] - the controller can be injected
* into other directives / components on the same DOM element.
* * `children` [NgDirective.CHILDREN_VISIBILITY] - the controller can be
* injected into other directives / components on the same or child DOM
* elements.
* * `direct_children` [NgDirective.DIRECT_CHILDREN_VISIBILITY] - the
* controller can be injected into other directives / components on the
* direct children of the current DOM element.
*/
final String visibility;
final List<Type> publishTypes;
/**
* Use map to define the mapping of component's DOM attributes into
* the component instance or scope. The map's key is the DOM attribute name
* to map in camelCase (DOM attribute is in dash-case). The Map's value
* consists of a mode character, and an expression. If expression is not
* specified, it maps to the same scope parameter as is the DOM attribute.
*
* * `@` - Map the DOM attribute string. The attribute string will be taken
* literally or interpolated if it contains binding {{}} systax.
*
* * `=` - Treat the DOM attribute value as an expression. Set up a watch
* on both outside as well as component scope to keep the src and
* destination in sync.
*
* * `!` - Treat the DOM attribute value as an expression. Set up a one time
* watch on expression. Once the expression turns truthy it will no longer
* update.
*
* * `&` - Treat the DOM attribute value as an expression. Assign a closure
* function into the component. This allows the component to control
* the invocation of the closure. This is useful for passing
* expressions into controllers which act like callbacks.
*
* NOTE: an expression may start with `.` which evaluates the expression
* against the current controller rather then current scope.
*
* Example:
*
* <my-component title="Hello {{username}}"
* selection="selectedItem"
* on-selection-change="doSomething()">
*
* @NgComponent(
* selector: 'my-component'
* map: const {
* 'title': '@.title',
* 'selection': '=.currentItem',
* 'onSelectionChange': '&.onChange'
* }
* )
* class MyComponent {
* String title;
* var currentItem;
* ParsedFn onChange;
* }
*
* The above example shows how all three mapping modes are used.
*
* * `@.title` maps the title DOM attribute to the controller `title`
* field. Notice that this maps the content of the attribute, which
* means that it can be used with `{{}}` interpolation.
*
* * `=.currentItem` maps the expression (in this case the `selectedItem`
* in the current scope into the `currentItem` in the controller. Notice
* that mapping is bi-directional. A change either in controller or on
* parent scope will result in change of the other.
*
* * `&.onChange` maps the expression into tho controllers `onChange`
* field. The result of mapping is a callable function which can be
* invoked at any time by the controller. The invocation of the
* callable function will result in the expression `doSomething()` to
* be executed in the parent context.
*/
final Map<String, String> map;
/**
* Use the list to specify expression containing attributes which are not
* included under [map] with '=' or '@' specification.
*/
final List<String> exportExpressionAttrs;
/**
* Use the list to specify a expressions which are evaluated dynamically
* (ex. via [Scope.$eval]) and are otherwise not statically discoverable.
*/
final List<String> exportExpressions;
/**
* An expression under which the controller instance will be published into.
* This allows the expressions in the template to be referring to controller
* instance and its properties.
*/
final String publishAs;
const NgAnnotation({
this.selector,
this.children: NgAnnotation.COMPILE_CHILDREN,
this.visibility: NgDirective.LOCAL_VISIBILITY,
this.publishAs,
this.publishTypes: const [],
this.map: const {},
this.exportExpressions: const [],
this.exportExpressionAttrs: const []
});
toString() => selector;
get hashCode => selector.hashCode;
operator==(other) =>
other is NgAnnotation && this.selector == other.selector;
}
/**
* Meta-data marker placed on a class which should act as a controller for the
* component. Angular components are a light-weight version of web-components.
* Angular components use shadow-DOM for rendering their templates.
*
* Angular components are instantiated using dependency injection, and can
* ask for any injectable object in their constructor. Components
* can also ask for other components or directives declared on the DOM element.
*
* Components can declared these optional methods:
*
* * `attach()` - Called on first [Scope.$digest()].
*
* * `detach()` - Called on when owning scope is destroyed.
*
*/
class NgComponent extends NgAnnotation {
/**
* Inlined HTML template for the component.
*/
final String template;
/**
* A URL to HTML template. This will be loaded asynchronously and
* cached for future component instances.
*/
final String templateUrl;
/**
* A CSS URL to load into the shadow DOM.
*/
final String cssUrl;
/**
* Set the shadow root applyAuthorStyles property. See shadow-DOM
* documentation for further details.
*/
final bool applyAuthorStyles;
/**
* Set the shadow root resetStyleInheritance property. See shadow-DOM
* documentation for further details.
*/
final bool resetStyleInheritance;
const NgComponent({
this.template,
this.templateUrl,
this.cssUrl,
this.applyAuthorStyles,
this.resetStyleInheritance,
publishAs,
map,
selector,
visibility,
publishTypes : const <Type>[],
exportExpressions,
exportExpressionAttrs
}) : super(selector: selector,
children: NgAnnotation.COMPILE_CHILDREN,
visibility: visibility,
publishTypes: publishTypes,
publishAs: publishAs,
map: map,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);
}
RegExp _ATTR_NAME = new RegExp(r'\[([^\]]+)\]$');
class NgDirective extends NgAnnotation {
static const String LOCAL_VISIBILITY = 'local';
static const String CHILDREN_VISIBILITY = 'children';
static const String DIRECT_CHILDREN_VISIBILITY = 'direct_children';
final String attrName;
const NgDirective({
children: NgAnnotation.COMPILE_CHILDREN,
this.attrName: null,
publishAs,
map,
selector,
visibility,
publishTypes : const <Type>[],
exportExpressions,
exportExpressionAttrs
}) : super(selector: selector, children: children, visibility: visibility,
publishTypes: publishTypes, publishAs: publishAs, map: map,
exportExpressions: exportExpressions,
exportExpressionAttrs: exportExpressionAttrs);
get defaultAttributeName {
if (attrName == null && selector != null) {
var match = _ATTR_NAME.firstMatch(selector);
return match != null ? match[1] : null;
}
return attrName;
}
}
/**
* Implementing directives or components [attach] method will be called when
* the next scope digest occurs after component instantiation. It is guaranteed
* that when [attach] is invoked, that all attribute mappings have already
* been processed.
*/
abstract class NgAttachAware {
void attach();
}
/**
* Implementing directives or components [detach] method will be called when
* the associated scope is destroyed.
*/
abstract class NgDetachAware {
void detach();
}
class DirectiveMap extends AnnotationMap<NgAnnotation> {
DirectiveMap(Injector injector, MetadataExtractor metadataExtractor)
: super(injector, metadataExtractor);
}
library angular.tools.utils;
camelcase(String s) {
var part = s.split('-').map((s) => s.toLowerCase());
if (part.length <= 1) {
return part.join();
}
return part.first + part.skip(1).map(capitalize).join();
}
capitalize(String s) => s.substring(0, 1).toUpperCase() + s.substring(1);
var SNAKE_CASE_REGEXP = new RegExp("[A-Z]");
snakecase(String name, [separator = '-']) =>
name.replaceAllMapped(SNAKE_CASE_REGEXP, (Match match) =>
(match.start != 0 ? separator : '') + match.group(0).toLowerCase());
library di.errors;
class NoProviderError extends ArgumentError {
NoProviderError(message) : super(message);
}
class CircularDependencyError extends ArgumentError {
CircularDependencyError(message) : super(message);
}
library perf_api;
import 'dart:async';
/**
* A simple profiler api.
*/
class Profiler {
/**
* Const constructor allows instances of this class to be used as a no-op
* implementation.
*/
const Profiler();
/**
* Starts a new timer for a given action [name]. An [int] timer id will be
* returned which can be used in [stopTimer] to stop the timer.
*
* [extraData] is additional information about the timed action. Implementing
* profiler should not assume any semantic or syntactic structure of that
* data and is free to ignore it in aggregate reports.
*/
int startTimer(String name, [String extraData]) => null;
/**
* Stop a timer for a given [idOrName]. If [idOrName] is [int] then it's
* treated as an action identifier returned from [startTimer]. If id is
* invalid or timer for that id was already stopped then [ProfilerError]
* will be thrown. If [idOrName] is [String] then the latest active timer
* with that name will be stopped. If no active timer exists then
* [ProfilerError] will be thrown.
*/
void stopTimer(dynamic idOrName) {}
/**
* A simple zero-duration marker.
*/
void markTime(String name, [String extraData]) {}
/**
* Times execution of the [functionOrFuture]. Body can either be a no argument
* function or a [Future]. If function, it is executed synchronously and its
* return value is returned. If it's a Future, then timing is stopped when the
* future completes either successfully or with error.
*/
dynamic time(String name, functionOrFuture, [String extraData]) {
var id = startTimer(name, extraData);
if (functionOrFuture is Function) {
try {
return functionOrFuture();
} finally {
stopTimer(id);
}
}
if (functionOrFuture is Future) {
return functionOrFuture.then(
(v) {
stopTimer(id);
return v;
},
onError: (e) {
stopTimer(id);
throw e;
});
}
throw new ProfilerError(
'Invalid functionOrFuture or type ${functionOrFuture.runtimeType}');
}
}
class ProfilerError extends Error {
final String message;
ProfilerError(String this.message);
toString() => message;
}
library angular.core.parser;
import 'dart:mirrors';
import 'package:perf_api/perf_api.dart';
import '../../utils.dart';
import '../module.dart';
part 'backend.dart';
part 'lexer.dart';
part 'parser.dart';
part 'static_parser.dart';
// Placeholder for DI.
// The parser you are looking for is DynamicParser
abstract class Parser {
call(String text) {}
primaryFromToken(Token token, parserError);
}
part of angular.directive;
/**
* The `ngClass` allows you to set CSS classes on HTML an element, dynamically,
* by databinding an expression that represents all classes to be added.
*
* The directive won't add duplicate classes if a particular class was
* already set.
*
* When the expression changes, the previously added classes are removed and
* only then the new classes are added.
*
* The result of the expression evaluation can be a string representing space
* delimited class names, an array, or a map of class names to boolean values.
* In the case of a map, the names of the properties whose values are truthy
* will be added as css classes to the element.
*
* ##Examples
*
* index.html:
*
* <p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p>
* <input type="checkbox" ng-model="bold"> bold
* <input type="checkbox" ng-model="strike"> strike
* <input type="checkbox" ng-model="red"> red
* <hr>
* <p ng-class="style">Using String Syntax</p>
* <input type="text" ng-model="style" placeholder="Type: bold strike red">
* <hr>
* <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
* <input ng-model="style1" placeholder="Type: bold"><br>
* <input ng-model="style2" placeholder="Type: strike"><br>
* <input ng-model="style3" placeholder="Type: red"><br>
*
* style.css:
*
* .strike {
* text-decoration: line-through;
* }
* .bold {
* font-weight: bold;
* }
* .red {
* color: red;
* }
*
*/
@NgDirective(
selector: '[ng-class]',
map: const {'ng-class': '@.valueExpression'})
class NgClassDirective extends _NgClassBase {
NgClassDirective(dom.Element element, Scope scope, NodeAttrs attrs)
: super(element, scope, null, attrs);
}
/**
* The `ngClassOdd` and `ngClassEven` directives work exactly as
* {@link ng.directive:ngClass ngClass}, except it works in
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
*
* This directive can be applied only within a scope of an `ngRepeat`.
*
* ##Examples
*
* index.html:
*
* <li ng-repeat="name in ['John', 'Mary', 'Cate', 'Suz']">
* <span ng-class-odd="'odd'" ng-class-even="'even'">
* {{name}}
* </span>
* </li>
*
* style.css:
*
* .odd {
* color: red;
* }
* .even {
* color: blue;
* }
*/
@NgDirective(
selector: '[ng-class-odd]',
map: const {'ng-class-odd': '@.valueExpression'})
class NgClassOddDirective extends _NgClassBase {
NgClassOddDirective(dom.Element element, Scope scope, NodeAttrs attrs)
: super(element, scope, 0, attrs);
}
/**
* The `ngClassOdd` and `ngClassEven` directives work exactly as
* {@link ng.directive:ngClass ngClass}, except it works in
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
*
* This directive can be applied only within a scope of an `ngRepeat`.
*
* ##Examples
*
* index.html:
*
* <li ng-repeat="name in ['John', 'Mary', 'Cate', 'Suz']">
* <span ng-class-odd="'odd'" ng-class-even="'even'">
* {{name}}
* </span>
* </li>
*
* style.css:
*
* .odd {
* color: red;
* }
* .even {
* color: blue;
* }
*/
@NgDirective(
selector: '[ng-class-even]',
map: const {'ng-class-even': '@.valueExpression'})
class NgClassEvenDirective extends _NgClassBase {
NgClassEvenDirective(dom.Element element, Scope scope, NodeAttrs attrs)
: super(element, scope, 1, attrs);
}
abstract class _NgClassBase {
final dom.Element element;
final Scope scope;
final int mode;
final NodeAttrs nodeAttrs;
var previousSet = [];
var currentSet = [];
_NgClassBase(this.element, this.scope, this.mode, this.nodeAttrs) {
var prevClass;
nodeAttrs.observe('class', (String newValue) {
if (prevClass != newValue) {
prevClass = newValue;
_handleChange(scope[r'$index']);
}
});
}
set valueExpression(currentExpression) {
// this should be called only once, so we don't worry about cleaning up
// watcher registrations.
scope.$watchCollection(currentExpression, (current) {
currentSet = _flatten(current);
_handleChange(scope[r'$index']);
});
if (mode != null) {
scope.$watch(r'$index', (index, oldIndex) {
var mod = index % 2;
if (oldIndex == null || mod != oldIndex % 2) {
if (mod == mode) {
element.classes.addAll(currentSet);
} else {
element.classes.removeAll(previousSet);
}
}
});
}
}
_handleChange(index) {
if (mode == null || (index != null && index % 2 == mode)) {
element.classes.removeAll(previousSet);
element.classes.addAll(currentSet);
}
previousSet = currentSet;
}
static List<String> _flatten(classes) {
if (classes == null) return [];
if (classes is List) {
return classes;
}
if (classes is Map) {
return classes.keys.where((key) => toBool(classes[key])).toList();
}
if (classes is String) {
return classes.split(' ');
}
throw 'ng-class expects expression value to be List, Map or String.';
}
}
part of angular.core;
abstract class AnnotationMap<K> {
final Map<K, Type> _map = {};
AnnotationMap(Injector injector, MetadataExtractor extractMetadata) {
injector.types.forEach((type) {
var meta = extractMetadata(type)
.where((annotation) => annotation is K)
.forEach((annotation) {
if (_map.containsKey(annotation)) {
var annotationType = K;
throw "Duplicate annotation found: $annotationType: $annotation. " +
"Exisitng: ${_map[annotation]}; New: $type.";
}
_map[annotation] = type;
});
});
}
Type operator[](K annotation) {
var value = _map[annotation];
if (value == null) {
throw 'No $annotation found!';
}
return value;
}
forEach(fn(K, Type)) => _map.forEach(fn);
}
class MetadataExtractor {
Iterable call(Type type) {
var metadata = reflectClass(type).metadata;
if (metadata == null) return [];
return metadata.map((InstanceMirror im) => im.reflectee);
}
}
part of angular.directive;
/**
* The ngSwitch directive is used to conditionally swap DOM structure on your
* template based on a scope expression. Elements within ngSwitch but without
* ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
* as specified in the template.
*
* The directive itself works similar to ngInclude, however, instead of
* downloading template code (or loading it from the template cache), ngSwitch
* simply choses one of the nested elements and makes it visible based on which
* element matches the value obtained from the evaluated expression. In other
* words, you define a container element (where you place the directive), place
* an expression on the **ng-switch="..." attribute**, define any inner elements
* inside of the directive and place a when attribute per element. The when
* attribute is used to inform ngSwitch which element to display when the on
* expression is evaluated. If a matching expression is not found via a when
* attribute then an element with the default attribute is displayed.
*
* ## Example:
*
* <ANY ng-switch="expression">
* <ANY ng-switch-when="matchValue1">...</ANY>
* <ANY ng-switch-when="matchValue2">...</ANY>
* <ANY ng-switch-default>...</ANY>
* </ANY>
*
* On child elements add:
*
* * `ngSwitchWhen`: the case statement to match against. If match then this
* case will be displayed. If the same match appears multiple times, all the
* elements will be displayed.
* * `ngSwitchDefault`: the default case when no other case match. If there
* are multiple default cases, all of them will be displayed when no other
* case match.
*
* ## Example:
*
* <div>
* <button ng-click="selection='settings'">Show Settings</button>
* <button ng-click="selection='home'">Show Home Span</button>
* <button ng-click="selection=''">Show default</button>
* <tt>selection={{selection}}</tt>
* <hr/>
* <div ng-switch="selection">
* <div ng-switch-when="settings">Settings Div</div>
* <div ng-switch-when="home">Home Span</div>
* <div ng-switch-default>default</div>
* </div>
* </div>
*/
@NgDirective(
selector: '[ng-switch]',
map: const {
'ng-switch': '=.value',
'change': '&.onChange'
},
visibility: NgDirective.DIRECT_CHILDREN_VISIBILITY
)
class NgSwitchDirective {
Map<String, List<_Case>> cases = new Map<String, List<_Case>>();
List<_BlockScopePair> currentBlocks = <_BlockScopePair>[];
Function onChange;
Scope scope;
NgSwitchDirective(Scope this.scope) {
cases['?'] = <_Case>[];
}
addCase(String value, BlockHole anchor, BoundBlockFactory blockFactory) {
cases.putIfAbsent(value, () => <_Case>[]);
cases[value].add(new _Case(anchor, blockFactory));
}
set value(val) {
currentBlocks
..forEach((_BlockScopePair pair) {
pair.block.remove();
pair.scope.$destroy();
})
..clear();
val = '!$val';
(cases.containsKey(val) ? cases[val] : cases['?'])
.forEach((_Case caze) {
Scope childScope = scope.$new();
var block = caze.blockFactory(childScope)
..insertAfter(caze.anchor);
currentBlocks.add(new _BlockScopePair(block, childScope));
});
if (onChange != null) {
onChange();
}
}
}
class _BlockScopePair {
final Block block;
final Scope scope;
_BlockScopePair(this.block, this.scope);
}
class _Case {
final BlockHole anchor;
final BoundBlockFactory blockFactory;
_Case(this.anchor, this.blockFactory);
}
@NgDirective(
selector: '[ng-switch-when]',
children: NgAnnotation.TRANSCLUDE_CHILDREN,
map: const {
'.': '@.value'
}
)
class NgSwitchWhenDirective {
final NgSwitchDirective ngSwitch;
final BlockHole hole;
final BoundBlockFactory blockFactory;
final Scope scope;
NgSwitchWhenDirective(NgSwitchDirective this.ngSwitch,
BlockHole this.hole, BoundBlockFactory this.blockFactory,
Scope this.scope);
set value(String value) =>
ngSwitch.addCase('!$value', hole, blockFactory);
}
@NgDirective(
children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector: '[ng-switch-default]'
)
class NgSwitchDefaultDirective {
NgSwitchDefaultDirective(NgSwitchDirective ngSwitch, BlockHole hole,
BoundBlockFactory blockFactory, Scope scope) {
ngSwitch.addCase('?', hole, blockFactory);
}
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
// TODO(efortuna): Customized pattern system -- suggested by i18n needs
// feedback on appropriateness.
/**
* DateFormat is for formatting and parsing dates in a locale-sensitive
* manner.
* It allows the user to choose from a set of standard date time formats as well
* as specify a customized pattern under certain locales. Date elements that
* vary across locales include month name, week name, field order, etc.
* We also allow the user to use any customized pattern to parse or format
* date-time strings under certain locales. Date elements that vary across
* locales include month name, weekname, field, order, etc.
*
* Formatting dates in the default "en_US" format does not require any
* initialization. e.g.
* print(new DateFormat.yMMMd().format(new Date.now()));
*
* But for other locales, the formatting data for the locale must be
* obtained. This can currently be done
* in one of three ways, determined by which library you import. In all cases,
* the "initializeDateFormatting" method must be called and will return a future
* that is complete once the locale data is available. The result of the future
* isn't important, but the data for that locale is available to the date
* formatting and parsing once it completes.
*
* The easiest option is that the data may be available locally, imported in a
* library that contains data for all the locales.
* import 'package:intl/date_symbol_data_local.dart';
* initializeDateFormatting("fr_FR", null).then((_) => runMyCode());
*
* If we are running outside of a browser, we may want to read the data
* from files in the file system.
* import 'package:intl/date_symbol_data_file.dart';
* initializeDateFormatting("de_DE", null).then((_) => runMyCode());
*
* If we are running in a browser, we may want to read the data from the
* server using the XmlHttpRequest mechanism.
* import 'package:intl/date_symbol_data_http_request.dart';
* initializeDateFormatting("pt_BR", null).then((_) => runMyCode());
*
* The code in example/basic/basic_example.dart shows a full example of
* using this mechanism.
*
* Once we have the locale data, we need to specify the particular format.
* This library uses the ICU/JDK date/time pattern specification both for
* complete format specifications and also the abbreviated "skeleton" form
* which can also adapt to different locales and is preferred where available.
*
* Skeletons: These can be specified either as the ICU constant name or as the
* skeleton to which it resolves. The supported set of skeletons is as follows
* ICU Name Skeleton
* -------- --------
* DAY d
* ABBR_WEEKDAY E
* WEEKDAY EEEE
* ABBR_STANDALONE_MONTH LLL
* STANDALONE_MONTH LLLL
* NUM_MONTH M
* NUM_MONTH_DAY Md
* NUM_MONTH_WEEKDAY_DAY MEd
* ABBR_MONTH MMM
* ABBR_MONTH_DAY MMMd
* ABBR_MONTH_WEEKDAY_DAY MMMEd
* MONTH MMMM
* MONTH_DAY MMMMd
* MONTH_WEEKDAY_DAY MMMMEEEEd
* ABBR_QUARTER QQQ
* QUARTER QQQQ
* YEAR y
* YEAR_NUM_MONTH yM
* YEAR_NUM_MONTH_DAY yMd
* YEAR_NUM_MONTH_WEEKDAY_DAY yMEd
* YEAR_ABBR_MONTH yMMM
* YEAR_ABBR_MONTH_DAY yMMMd
* YEAR_ABBR_MONTH_WEEKDAY_DAY yMMMEd
* YEAR_MONTH yMMMM
* YEAR_MONTH_DAY yMMMMd
* YEAR_MONTH_WEEKDAY_DAY yMMMMEEEEd
* YEAR_ABBR_QUARTER yQQQ
* YEAR_QUARTER yQQQQ
* HOUR24 H
* HOUR24_MINUTE Hm
* HOUR24_MINUTE_SECOND Hms
* HOUR j
* HOUR_MINUTE jm
* HOUR_MINUTE_SECOND jms
* HOUR_MINUTE_GENERIC_TZ jmv
* HOUR_MINUTE_TZ jmz
* HOUR_GENERIC_TZ jv
* HOUR_TZ jz
* MINUTE m
* MINUTE_SECOND ms
* SECOND s
*
* Examples Using the US Locale:
*
* Pattern Result
* ---------------- -------
* new DateFormat.yMd() -> 7/10/1996
* new DateFormat("yMd") -> 7/10/1996
* new DateFormat.yMMMMd("en_US") -> July 10, 1996
* new DateFormat("Hm", "en_US") -> 12:08 PM
* new DateFormat.yMd().add_Hm() -> 7/10/1996 12:08 PM
*
* Explicit Pattern Syntax: Formats can also be specified with a pattern string.
* The skeleton forms will resolve to explicit patterns of this form, but will
* also adapt to different patterns in different locales.
* The following characters are reserved:
*
* Symbol Meaning Presentation Example
* ------ ------- ------------ -------
* G era designator (Text) AD
* y year (Number) 1996
* M month in year (Text & Number) July & 07
* L standalone month (Text & Number) July & 07
* d day in month (Number) 10
* c standalone day (Number) 10
* h hour in am/pm (1~12) (Number) 12
* H hour in day (0~23) (Number) 0
* m minute in hour (Number) 30
* s second in minute (Number) 55
* S fractional second (Number) 978
* E day of week (Text) Tuesday
* D day in year (Number) 189
* a am/pm marker (Text) PM
* k hour in day (1~24) (Number) 24
* K hour in am/pm (0~11) (Number) 0
* z time zone (Text) Pacific Standard Time
* Z time zone (RFC 822) (Number) -0800
* v time zone (generic) (Text) Pacific Time
* Q quarter (Text) Q3
* ' escape for text (Delimiter) 'Date='
* '' single quote (Literal) 'o''clock'
*
* The count of pattern letters determine the format.
*
* **Text**:
* * 5 pattern letters--use narrow form for standalone. Otherwise does not apply
* * 4 or more pattern letters--use full form,
* * 3 pattern letters--use short or abbreviated form if one exists
* * less than 3--use numeric form if one exists
*
* **Number**: the minimum number of digits. Shorter numbers are zero-padded to
* this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
* specially; that is, if the count of 'y' is 2, the Year will be truncated to
* 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
* fields, fractional seconds are padded on the right with zero.
*
* **(Text & Number)**: 3 or over, use text, otherwise use number.
*
* Any characters not in the pattern will be treated as quoted text. For
* instance, characters like ':', '.', ' ', '#' and '@' will appear in the
* resulting text even though they are not enclosed in single quotes. In our
* current pattern usage, not all letters have meanings. But those unused
* letters are strongly discouraged to be used as quoted text without quotes,
* because we may use other letters as pattern characters in the future.
*
* Examples Using the US Locale:
*
* Format Pattern Result
* -------------- -------
* "yyyy.MM.dd G 'at' HH:mm:ss vvvv" 1996.07.10 AD at 15:08:56 Pacific Time
* "EEE, MMM d, ''yy" Wed, July 10, '96
* "h:mm a" 12:08 PM
* "hh 'o''clock' a, zzzz" 12 o'clock PM, Pacific Daylight Time
* "K:mm a, vvv" 0:00 PM, PT
* "yyyyy.MMMMM.dd GGG hh:mm aaa" 01996.July.10 AD 12:08 PM
*
* When parsing a date string using the abbreviated year pattern ("yy"),
* DateFormat must interpret the abbreviated year relative to some
* century. It does this by adjusting dates to be within 80 years before and 20
* years after the time the parse function is called. For example, using a
* pattern of "MM/dd/yy" and a DateParse instance created on Jan 1, 1997,
* the string "01/11/12" would be interpreted as Jan 11, 2012 while the string
* "05/04/64" would be interpreted as May 4, 1964. During parsing, only
* strings consisting of exactly two digits, as defined by {@link
* java.lang.Character#isDigit(char)}, will be parsed into the default
* century. Any other numeric string, such as a one digit string, a three or
* more digit string will be interpreted as its face value.
*
* If the year pattern does not have exactly two 'y' characters, the year is
* interpreted literally, regardless of the number of digits. So using the
* pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
*/
class DateFormat {
/**
* Creates a new DateFormat, using the format specified by [newPattern]. For
* forms that match one of our predefined skeletons, we look up the
* corresponding pattern in [locale] (or in the default locale if none is
* specified) and use the resulting full format string. This is the
* preferred usage, but if [newPattern] does not match one of the skeletons,
* then it is used as a format directly, but will not be adapted to suit
* the locale.
*
* For example, in an en_US locale, specifying the skeleton
* new DateFormat('yMEd');
* or the explicit
* new DateFormat('EEE, M/d/y');
* would produce the same result, a date of the form
* Wed, 6/27/2012
* The first version would produce a different format string if used in
* another locale, but the second format would always be the same.
*
* If [locale] does not exist in our set of supported locales then an
* [ArgumentError] is thrown.
*/
DateFormat([String newPattern, String locale]) {
// TODO(alanknight): It should be possible to specify multiple skeletons eg
// date, time, timezone all separately. Adding many or named parameters to
// the constructor seems awkward, especially with the possibility of
// confusion with the locale. A "fluent" interface with cascading on an
// instance might work better? A list of patterns is also possible.
_locale = Intl.verifiedLocale(locale, localeExists);
addPattern(newPattern);
}
/**
* Return a string representing [date] formatted according to our locale
* and internal format.
*/
String format(DateTime date) {
// TODO(efortuna): read optional TimeZone argument (or similar)?
var result = new StringBuffer();
_formatFields.forEach((field) => result.write(field.format(date)));
return result.toString();
}
/**
* Returns a date string indicating how long ago (3 hours, 2 minutes)
* something has happened or how long in the future something will happen
* given a [reference] DateTime relative to the current time.
*/
String formatDuration(DateTime reference) {
return '';
}
/**
* Formats a string indicating how long ago (negative [duration]) or how far
* in the future (positive [duration]) some time is with respect to a
* reference [date].
*/
String formatDurationFrom(Duration duration, DateTime date) {
return '';
}
/**
* Given user input, attempt to parse the [inputString] into the anticipated
* format, treating it as being in the local timezone. If [inputString] does
* not match our format, throws a [FormatException].
*/
DateTime parse(String inputString, [utc = false]) {
// TODO(alanknight): The Closure code refers to special parsing of numeric
// values with no delimiters, which we currently don't do. Should we?
var dateFields = new _DateBuilder();
if (utc) dateFields.utc=true;
var stream = new _Stream(inputString);
_formatFields.forEach(
(each) => each.parse(stream, dateFields));
return dateFields.asDate();
}
/**
* Given user input, attempt to parse the [inputString] into the anticipated
* format, treating it as being in UTC.
*/
DateTime parseUTC(String inputString) {
return parse(inputString, true);
}
/**
* Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
*/
String get locale => _locale;
/**
* Returns a list of all locales for which we have date formatting
* information.
*/
static List<String> allLocalesWithSymbols() => dateTimeSymbols.keys.toList();
/**
* The named constructors for this class are all conveniences for creating
* instances using one of the known "skeleton" formats, and having code
* completion support for discovering those formats.
* So,
* new DateFormat.yMd("en_US")
* is equivalent to
* new DateFormat("yMd", "en_US")
* To create a compound format you can use these constructors in combination
* with the add_ methods below. e.g.
* new DateFormat.yMd().add_Hms();
* If the optional [locale] is omitted, the format will be created using the
* default locale in [Intl.systemLocale].
*/
DateFormat.d([locale]) : this("d", locale);
DateFormat.E([locale]) : this("E", locale);
DateFormat.EEEE([locale]) : this("EEEE", locale);
DateFormat.LLL([locale]) : this("LLL", locale);
DateFormat.LLLL([locale]) : this("LLLL", locale);
DateFormat.M([locale]) : this("M", locale);
DateFormat.Md([locale]) : this("Md", locale);
DateFormat.MEd([locale]) : this("MEd", locale);
DateFormat.MMM([locale]) : this("MMM", locale);
DateFormat.MMMd([locale]) : this("MMMd", locale);
DateFormat.MMMEd([locale]) : this("MMMEd", locale);
DateFormat.MMMM([locale]) : this("MMMM", locale);
DateFormat.MMMMd([locale]) : this("MMMMd", locale);
DateFormat.MMMMEEEEd([locale]) : this("MMMMEEEEd", locale);
DateFormat.QQQ([locale]) : this("QQQ", locale);
DateFormat.QQQQ([locale]) : this("QQQQ", locale);
DateFormat.y([locale]) : this("y", locale);
DateFormat.yM([locale]) : this("yM", locale);
DateFormat.yMd([locale]) : this("yMd", locale);
DateFormat.yMEd([locale]) : this("yMEd", locale);
DateFormat.yMMM([locale]) : this("yMMM", locale);
DateFormat.yMMMd([locale]) : this("yMMMd", locale);
DateFormat.yMMMEd([locale]) : this("yMMMEd", locale);
DateFormat.yMMMM([locale]) : this("yMMMM", locale);
DateFormat.yMMMMd([locale]) : this("yMMMMd", locale);
DateFormat.yMMMMEEEEd([locale]) : this("yMMMMEEEEd", locale);
DateFormat.yQQQ([locale]) : this("yQQQ", locale);
DateFormat.yQQQQ([locale]) : this("yQQQQ", locale);
DateFormat.H([locale]) : this("H", locale);
DateFormat.Hm([locale]) : this("Hm", locale);
DateFormat.Hms([locale]) : this("Hms", locale);
DateFormat.j([locale]) : this("j", locale);
DateFormat.jm([locale]) : this("jm", locale);
DateFormat.jms([locale]) : this("jms", locale);
DateFormat.jmv([locale]) : this("jmv", locale);
DateFormat.jmz([locale]) : this("jmz", locale);
DateFormat.jv([locale]) : this("jv", locale);
DateFormat.jz([locale]) : this("jz", locale);
DateFormat.m([locale]) : this("m", locale);
DateFormat.ms([locale]) : this("ms", locale);
DateFormat.s([locale]) : this("s", locale);
/**
* The "add_*" methods append a particular skeleton to the format, or set
* it as the only format if none was previously set. These are primarily
* useful for creating compound formats. For example
* new DateFormat.yMd().add_Hms();
* would create a date format that prints both the date and the time.
*/
DateFormat add_d() => addPattern("d");
DateFormat add_E() => addPattern("E");
DateFormat add_EEEE() => addPattern("EEEE");
DateFormat add_LLL() => addPattern("LLL");
DateFormat add_LLLL() => addPattern("LLLL");
DateFormat add_M() => addPattern("M");
DateFormat add_Md() => addPattern("Md");
DateFormat add_MEd() => addPattern("MEd");
DateFormat add_MMM() => addPattern("MMM");
DateFormat add_MMMd() => addPattern("MMMd");
DateFormat add_MMMEd() => addPattern("MMMEd");
DateFormat add_MMMM() => addPattern("MMMM");
DateFormat add_MMMMd() => addPattern("MMMMd");
DateFormat add_MMMMEEEEd() => addPattern("MMMMEEEEd");
DateFormat add_QQQ() => addPattern("QQQ");
DateFormat add_QQQQ() => addPattern("QQQQ");
DateFormat add_y() => addPattern("y");
DateFormat add_yM() => addPattern("yM");
DateFormat add_yMd() => addPattern("yMd");
DateFormat add_yMEd() => addPattern("yMEd");
DateFormat add_yMMM() => addPattern("yMMM");
DateFormat add_yMMMd() => addPattern("yMMMd");
DateFormat add_yMMMEd() => addPattern("yMMMEd");
DateFormat add_yMMMM() => addPattern("yMMMM");
DateFormat add_yMMMMd() => addPattern("yMMMMd");
DateFormat add_yMMMMEEEEd() => addPattern("yMMMMEEEEd");
DateFormat add_yQQQ() => addPattern("yQQQ");
DateFormat add_yQQQQ() => addPattern("yQQQQ");
DateFormat add_H() => addPattern("H");
DateFormat add_Hm() => addPattern("Hm");
DateFormat add_Hms() => addPattern("Hms");
DateFormat add_j() => addPattern("j");
DateFormat add_jm() => addPattern("jm");
DateFormat add_jms() => addPattern("jms");
DateFormat add_jmv() => addPattern("jmv");
DateFormat add_jmz() => addPattern("jmz");
DateFormat add_jv() => addPattern("jv");
DateFormat add_jz() => addPattern("jz");
DateFormat add_m() => addPattern("m");
DateFormat add_ms() => addPattern("ms");
DateFormat add_s() => addPattern("s");
/**
* For each of the skeleton formats we also allow the use of the corresponding
* ICU constant names.
*/
static const String ABBR_MONTH = 'MMM';
static const String DAY = 'd';
static const String ABBR_WEEKDAY = 'E';
static const String WEEKDAY = 'EEEE';
static const String ABBR_STANDALONE_MONTH = 'LLL';
static const String STANDALONE_MONTH = 'LLLL';
static const String NUM_MONTH = 'M';
static const String NUM_MONTH_DAY = 'Md';
static const String NUM_MONTH_WEEKDAY_DAY = 'MEd';
static const String ABBR_MONTH_DAY = 'MMMd';
static const String ABBR_MONTH_WEEKDAY_DAY = 'MMMEd';
static const String MONTH = 'MMMM';
static const String MONTH_DAY = 'MMMMd';
static const String MONTH_WEEKDAY_DAY = 'MMMMEEEEd';
static const String ABBR_QUARTER = 'QQQ';
static const String QUARTER = 'QQQQ';
static const String YEAR = 'y';
static const String YEAR_NUM_MONTH = 'yM';
static const String YEAR_NUM_MONTH_DAY = 'yMd';
static const String YEAR_NUM_MONTH_WEEKDAY_DAY = 'yMEd';
static const String YEAR_ABBR_MONTH = 'yMMM';
static const String YEAR_ABBR_MONTH_DAY = 'yMMMd';
static const String YEAR_ABBR_MONTH_WEEKDAY_DAY = 'yMMMEd';
static const String YEAR_MONTH = 'yMMMM';
static const String YEAR_MONTH_DAY = 'yMMMMd';
static const String YEAR_MONTH_WEEKDAY_DAY = 'yMMMMEEEEd';
static const String YEAR_ABBR_QUARTER = 'yQQQ';
static const String YEAR_QUARTER = 'yQQQQ';
static const String HOUR24 = 'H';
static const String HOUR24_MINUTE = 'Hm';
static const String HOUR24_MINUTE_SECOND = 'Hms';
static const String HOUR = 'j';
static const String HOUR_MINUTE = 'jm';
static const String HOUR_MINUTE_SECOND = 'jms';
static const String HOUR_MINUTE_GENERIC_TZ = 'jmv';
static const String HOUR_MINUTE_TZ = 'jmz';
static const String HOUR_GENERIC_TZ = 'jv';
static const String HOUR_TZ = 'jz';
static const String MINUTE = 'm';
static const String MINUTE_SECOND = 'ms';
static const String SECOND = 's';
/** The locale in which we operate, e.g. 'en_US', or 'pt'. */
String _locale;
/**
* The full template string. This may have been specified directly, or
* it may have been derived from a skeleton and the locale information
* on how to interpret that skeleton.
*/
String _pattern;
/**
* We parse the format string into individual [_DateFormatField] objects
* that are used to do the actual formatting and parsing. Do not use
* this variable directly, use the getter [_formatFields].
*/
List<_DateFormatField> _formatFieldsPrivate;
/**
* Getter for [_formatFieldsPrivate] that lazily initializes it.
*/
get _formatFields {
if (_formatFieldsPrivate == null) {
if (_pattern == null) _useDefaultPattern();
_formatFieldsPrivate = parsePattern(_pattern);
}
return _formatFieldsPrivate;
}
/**
* We are being asked to do formatting without having set any pattern.
* Use a default.
*/
_useDefaultPattern() {
add_yMMMMd();
add_jms();
}
/**
* A series of regular expressions used to parse a format string into its
* component fields.
*/
static List<RegExp> _matchers = [
// Quoted String - anything between single quotes, with escaping
// of single quotes by doubling them.
// e.g. in the pattern "hh 'o''clock'" will match 'o''clock'
new RegExp("^\'(?:[^\']|\'\')*\'"),
// Fields - any sequence of 1 or more of the same field characters.
// e.g. in "hh:mm:ss" will match hh, mm, and ss. But in "hms" would
// match each letter individually.
new RegExp(
"^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|D+|m+|s+|v+|z+|Z+)"),
// Everything else - A sequence that is not quotes or field characters.
// e.g. in "hh:mm:ss" will match the colons.
new RegExp("^[^\'GyMkSEahKHcLQdDmsvzZ]+")
];
/**
* Set our pattern, appending it to any existing patterns. Also adds a single
* space to separate the two.
*/
_appendPattern(String inputPattern, [String separator = ' ']) {
if (_pattern == null) {
_pattern = inputPattern;
} else {
_pattern = "$_pattern$separator$inputPattern";
}
}
/**
* Add [inputPattern] to this instance as a pattern. If there was a previous
* pattern, then this appends to it, separating the two by [separator].
* [inputPattern] is first looked up in our list of known skeletons.
* If it's found there, then use the corresponding pattern for this locale.
* If it's not, then treat [inputPattern] as an explicit pattern.
*/
DateFormat addPattern(String inputPattern, [String separator = ' ']) {
// TODO(alanknight): This is an expensive operation. Caching recently used
// formats, or possibly introducing an entire "locale" object that would
// cache patterns for that locale could be a good optimization.
// If we have already parsed the format fields, reset them.
_formatFieldsPrivate = null;
if (inputPattern == null) return this;
if (!_availableSkeletons.containsKey(inputPattern)) {
_appendPattern(inputPattern, separator);
} else {
_appendPattern(_availableSkeletons[inputPattern], separator);
}
return this;
}
/** Return the pattern that we use to format dates.*/
get pattern => _pattern;
/** Return the skeletons for our current locale. */
Map get _availableSkeletons {
return dateTimePatterns[locale];
}
/**
* Return the [DateSymbol] information for the locale. This can be useful
* to find lists like the names of weekdays or months in a locale, but
* the structure of this data may change, and it's generally better to go
* through the [format] and [parse] APIs. If the locale isn't present, or
* is uninitialized, returns null;
*/
DateSymbols get dateSymbols => dateTimeSymbols[_locale];
/**
* Set the locale. If the locale can't be found, we also look up
* based on alternative versions, e.g. if we have no 'en_CA' we will
* look for 'en' as a fallback. It will also translate en-ca into en_CA.
* Null is also considered a valid value for [newLocale], indicating
* to use the default.
*/
_setLocale(String newLocale) {
_locale = Intl.verifiedLocale(newLocale, localeExists);
}
/**
* Return true if the locale exists, or if it is null. The null case
* is interpreted to mean that we use the default locale.
*/
static bool localeExists(localeName) {
if (localeName == null) return false;
return dateTimeSymbols.containsKey(localeName);
}
static List get _fieldConstructors => [
(pattern, parent) => new _DateFormatQuotedField(pattern, parent),
(pattern, parent) => new _DateFormatPatternField(pattern, parent),
(pattern, parent) => new _DateFormatLiteralField(pattern, parent)];
/** Parse the template pattern and return a list of field objects.*/
List parsePattern(String pattern) {
if (pattern == null) return null;
return _parsePatternHelper(pattern).reversed.toList();
}
/** Recursive helper for parsing the template pattern. */
List _parsePatternHelper(String pattern) {
if (pattern.isEmpty) return [];
var matched = _match(pattern);
if (matched == null) return [];
var parsed = _parsePatternHelper(
pattern.substring(matched.fullPattern().length));
parsed.add(matched);
return parsed;
}
/** Find elements in a string that are patterns for specific fields.*/
_DateFormatField _match(String pattern) {
for (var i = 0; i < _matchers.length; i++) {
var regex = _matchers[i];
var match = regex.firstMatch(pattern);
if (match != null) {
return _fieldConstructors[i](match.group(0), this);
}
}
}
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* A library for general helper code associated with the intl library
* rather than confined to specific parts of it.
*/
library intl_helpers;
import 'dart:async';
/**
* This is used as a marker for a locale data map that hasn't been initialized,
* and will throw an exception on any usage that isn't the fallback
* patterns/symbols provided.
*/
class UninitializedLocaleData<F> {
final String message;
final F fallbackData;
const UninitializedLocaleData(this.message, this.fallbackData);
operator [](String key) =>
(key == 'en_US') ? fallbackData : _throwException();
String lookupMessage(String message_str, [final String desc='',
final Map examples=const {}, String locale,
String name, List<String> args]) => message_str;
List get keys => _throwException();
bool containsKey(String key) => (key == 'en_US') ? true : _throwException();
_throwException() {
throw new LocaleDataException("Locale data has not been initialized"
", call $message.");
}
}
class LocaleDataException implements Exception {
final String message;
LocaleDataException(this.message);
toString() => "LocaleDataException: $message";
}
/**
* An abstract superclass for data readers to keep the type system happy.
*/
abstract class LocaleDataReader {
Future read(String locale);
}
/**
* The internal mechanism for looking up messages. We expect this to be set
* by the implementing package so that we're not dependent on its
* implementation.
*/
var messageLookup = const
UninitializedLocaleData('initializeMessages(<locale>)', null);
/**
* Initialize the message lookup mechanism. This is for internal use only.
* User applications should import `message_lookup_by_library.dart` and call
* `initializeMessages`
*/
void initializeInternalMessageLookup(Function lookupFunction) {
if (messageLookup is UninitializedLocaleData) {
messageLookup = lookupFunction();
}
}
import 'package:angular/angular.dart';
@NgDirective(selector: '[toy-data]')
class ToyData {
ToyData(Scope scope) {
scope.friends = [{'name':'John', 'phone':'555-1276'},
{'name':'Mary', 'phone':'800-BIG-MARY'},
{'name':'Mike', 'phone':'555-4321'},
{'name':'Adam', 'phone':'555-5678'},
{'name':'Julie', 'phone':'555-8765'},
{'name':'Juliette', 'phone':'555-5678'}];
}
}
main() {
bootstrapAngular([new AngularModule()..type(ToyData)], 'html');
}
part of angular.filter;
/**
* Creates a new List or String containing only a prefix/suffix of the
* elements as specified by the `limit` parameter.
*
* When operating on a List, the returned list is always a copy even when all
* the elements are being returned.
*
* When the `limit` expression evaluates to a positive integer, `limit` items
* from the beginning of the List/String are returned. When `limit` evaluates
* to a negative integer, `|limit|` items from the end of the List/String are
* returned. If `|limit|` is larger than the size of the List/String, then the
* entire List/String is returned. In the case of a List, a copy of the list is
* returned.
*
* If the `limit` expression evaluates to a null or non-integer, then an empty
* list is returned. If the input is a null List/String, a null is returned.
*
* Example:
*
* - `{{ 'abcdefghij' | limitTo: 4 }}` → `'abcd'`
* - `{{ 'abcdefghij' | limitTo: -4 }}` → `'ghij'`
* - `{{ 'abcdefghij' | limitTo: -100 }}` → `'abcdefghij'`
*
* <br>
*
* This [ng-repeat] directive:
*
* <li ng-repeat="i in 'abcdefghij' | limitTo:-2">{{i}}</li>
*
* results in
*
* <li>i</li>
* <li>j</li>
*/
@NgFilter(name:'limitTo')
class LimitToFilter {
Injector _injector;
Parser _parser;
LimitToFilter(Injector this._injector, Parser this._parser);
dynamic call(dynamic items, [int limit]) {
if (items == null) {
return null;
}
if (limit == null) {
return const[];
}
if (items is! List && items is! String) {
return items;
}
int i = 0, j = items.length;
if (limit > -1) {
j = (limit > j) ? j : limit;
} else {
i = j + limit;
if (i < 0) {
i = 0;
}
}
if (items is String) {
return (items as String).substring(i, j);
} else {
return (items as List).getRange(i, j).toList(growable: false);
}
}
}
library angular.util;
toBool(x) {
if (x is bool) return x;
if (x is int || x is double) return x != 0;
return false;
}
typedef FnWith0Args();
typedef FnWith1Args(a0);
typedef FnWith2Args(a0, a1);
typedef FnWith3Args(a0, a1, a2);
typedef FnWith4Args(a0, a1, a2, a3);
typedef FnWith5Args(a0, a1, a2, a3, a4);
relaxFnApply(Function fn, List args) {
// Check the args.length to support functions with optional parameters.
var argsLen = args.length;
if (fn is Function && fn != null) {
if (fn is FnWith5Args && argsLen > 4) {
return fn(args[0], args[1], args[2], args[3], args[4]);
} else if (fn is FnWith4Args && argsLen > 3) {
return fn(args[0], args[1], args[2], args[3]);
} else if (fn is FnWith3Args && argsLen > 2 ) {
return fn(args[0], args[1], args[2]);
} else if (fn is FnWith2Args && argsLen > 1 ) {
return fn(args[0], args[1]);
} else if (fn is FnWith1Args && argsLen > 0) {
return fn(args[0]);
} else if (fn is FnWith0Args) {
return fn();
} else {
throw "Unknown function type, expecting 0 to 5 args.";
}
} else {
throw "Missing function.";
}
}
relaxFnArgs1(Function fn) {
if (fn is FnWith3Args) return (_1) => fn(_1, null, null);
if (fn is FnWith2Args) return (_1) => fn(_1, null);
if (fn is FnWith1Args) return fn;
if (fn is FnWith0Args) return (_1) => fn();
}
relaxFnArgs3(Function fn) {
if (fn is FnWith3Args) return fn;
if (fn is FnWith2Args) return (_1, _2, _3) => fn(_1, null);
if (fn is FnWith1Args) return (_1, _2, _3) => fn(_1);
if (fn is FnWith0Args) return (_1, _2, _3) => fn();
}
relaxFnArgs(Function fn) {
if (fn is FnWith5Args) {
return ([a0, a1, a2, a3, a4]) => fn(a0, a1, a2, a3, a4);
} else if (fn is FnWith4Args) {
return ([a0, a1, a2, a3, a4]) => fn(a0, a1, a2, a3);
} else if (fn is FnWith3Args) {
return ([a0, a1, a2, a3, a4]) => fn(a0, a1, a2);
} else if (fn is FnWith2Args) {
return ([a0, a1, a2, a3, a4]) => fn(a0, a1);
} else if (fn is FnWith1Args) {
return ([a0, a1, a2, a3, a4]) => fn(a0);
} else if (fn is FnWith0Args) {
return ([a0, a1, a2, a3, a4]) => fn();
} else {
return ([a0, a1, a2, a3, a4]) {
throw "Unknown function type, expecting 0 to 5 args.";
};
}
}
part of angular.core.dom;
@NgDirective(selector: r':contains(/{{.*}}/)')
class NgTextMustacheDirective {
dom.Node element;
Expression interpolateFn;
// This Directive is special and does not go through injection.
NgTextMustacheDirective(dom.Node this.element,
String markup,
Interpolate interpolate,
Scope scope,
TextChangeListener listener) {
interpolateFn = interpolate(markup);
setter(text) {
element.text = text;
if (listener != null) listener.call(text);
}
setter('');
scope.$watch(interpolateFn.eval, setter);
}
}
@NgDirective(selector: r'[*=/{{.*}}/]')
class NgAttrMustacheDirective {
static RegExp ATTR_NAME_VALUE_REGEXP = new RegExp(r'^([^=]+)=(.*)$');
// This Directive is special and does not go through injection.
NgAttrMustacheDirective(NodeAttrs attrs, String markup, Interpolate interpolate, Scope scope) {
var match = ATTR_NAME_VALUE_REGEXP.firstMatch(markup);
var attrName = match[1];
Expression interpolateFn = interpolate(match[2]);
Function attrSetter = (text) => attrs[attrName] = text;
attrSetter('');
scope.$watch(interpolateFn.eval, attrSetter);
}
}
part of angular.core;
/**
* Use @[NgFilter] annotation to register a new filter. A filter is a class
* with a [call] method (a callable function).
*
* Usage:
*
* // Declaration
* @NgFilter(name:'myFilter')
* class MyFilter {
* call(valueToFilter, optArg1, optArg2) {
* return ...;
* }
* }
*
*
* // Registration
* var module = ...;
* module.type(MyFilter);
*
*
* <!-- Usage -->
* <span>{{something | myFilter:arg1:arg2}}</span>
*/
class NgFilter {
final String name;
const NgFilter({String this.name});
int get hashCode => name.hashCode;
bool operator==(other) => this.name == other.name;
toString() => 'NgFilter: $name';
}
/**
* Registry of filters at runtime.
*/
class FilterMap extends AnnotationMap<NgFilter> {
Injector _injector;
FilterMap(Injector injector, MetadataExtractor extractMetadata) : super(injector, extractMetadata) {
this._injector = injector;
}
call(String name) {
var filter = new NgFilter(name:name);
var filterType = this[filter];
return _injector.get(filterType);
}
}
library angular.source_metadata_extractor ;
import 'package:analyzer_experimental/src/generated/ast.dart';
import 'source_crawler.dart';
import 'utils.dart';
import 'common.dart';
const String _COMPONENT = '-component';
const String _DIRECTIVE = '-directive';
String _ATTR_DIRECTIVE = '-attr' + _DIRECTIVE;
RegExp _ATTR_SELECTOR_REGEXP = new RegExp(r'\[([^\]]+)\]');
class SourceMetadataExtractor {
SourceCrawler sourceCrawler;
DirectiveMetadataCollectingVisitor metadataVisitor;
SourceMetadataExtractor(this.sourceCrawler,
[DirectiveMetadataCollectingVisitor this.metadataVisitor]) {
if (metadataVisitor == null) {
metadataVisitor = new DirectiveMetadataCollectingVisitor();
}
}
List<DirectiveInfo> gatherDirectiveInfo(root) {
sourceCrawler.crawl(root, metadataVisitor);
List<DirectiveInfo> directives = <DirectiveInfo>[];
metadataVisitor.metadata.forEach((DirectiveMetadata meta) {
DirectiveInfo dirInfo = new DirectiveInfo();
dirInfo.selector = meta.selector;
meta.attributeMappings.forEach((attrName, mappingSpec) {
if (mappingSpec.startsWith('=') ||
mappingSpec.startsWith('&') ||
mappingSpec.startsWith('@') ||
mappingSpec.startsWith('!')) {
dirInfo.expressionAttrs.add(snakecase(attrName));
}
if (mappingSpec.length == 1) { // shorthand
// TODO(pavelgj): Figure out if short-hand LHS should be expanded
// and added to the expressions list.
if (attrName != '.') {
dirInfo.expressions.add(attrName);
}
} else {
mappingSpec = mappingSpec.substring(1);
if (mappingSpec.startsWith('.')) {
mappingSpec = mappingSpec.substring(1);
}
dirInfo.expressions.add(mappingSpec);
}
});
meta.exportExpressionAttrs.forEach((attr) {
attr = snakecase(attr);
if (!dirInfo.expressionAttrs.contains(attr)) {
dirInfo.expressionAttrs.add(attr);
}
});
meta.exportExpressions.forEach((expr) {
if (!dirInfo.expressions.contains(expr)) {
dirInfo.expressions.add(expr);
}
});
// No explicit selector specified on the directive, compute one.
var className = snakecase(meta.className);
if (dirInfo.selector == null) {
if (meta.type == COMPONENT) {
if (className.endsWith(_COMPONENT)) {
dirInfo.selector = className.
substring(0, className.length - _COMPONENT.length);
} else {
throw "Directive name '$className' must end with $_DIRECTIVE, "
"$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
}
} else {
if (className.endsWith(_ATTR_DIRECTIVE)) {
var attrName = className.
substring(0, className.length - _ATTR_DIRECTIVE.length);
dirInfo.selector = '[$attrName]';
} else if (className.endsWith(_DIRECTIVE)) {
dirInfo.selector = className.
substring(0, className.length - _DIRECTIVE.length);
} else {
throw "Directive name '$className' must end with $_DIRECTIVE, "
"$_ATTR_DIRECTIVE, $_COMPONENT or have a \$selector field.";
}
}
}
var reprocessedAttrs = <String>[];
dirInfo.expressionAttrs.forEach((String attr) {
if (attr == '.') {
var matches = _ATTR_SELECTOR_REGEXP.allMatches(dirInfo.selector);
if (matches.length > 0) {
reprocessedAttrs.add(matches.last.group(1));
}
} else {
reprocessedAttrs.add(attr);
}
});
dirInfo.expressionAttrs = reprocessedAttrs;
directives.add(dirInfo);
});
return directives;
}
}
class DirectiveMetadataCollectingVisitor {
List<DirectiveMetadata> metadata = <DirectiveMetadata>[];
call(CompilationUnit cu) {
cu.declarations.forEach((CompilationUnitMember declaration) {
// We only care about classes.
if (declaration is! ClassDeclaration) return;
ClassDeclaration clazz = declaration;
clazz.metadata.forEach((Annotation ann) {
if (ann.arguments == null) return; // Ignore non-class annotations.
// TODO(pavelj): this is not a safe check for the type of the
// annotations, but good enough for now.
if (ann.name.name != 'NgComponent'
&& ann.name.name != 'NgDirective') return;
bool isComponent = ann.name.name == 'NgComponent';
DirectiveMetadata meta = new DirectiveMetadata()
..className = clazz.name.name
..type = isComponent ? COMPONENT : DIRECTIVE;
metadata.add(meta);
ann.arguments.arguments.forEach((Expression arg) {
if (arg is NamedExpression) {
NamedExpression namedArg = arg;
var paramName = namedArg.name.label.name;
if (paramName == 'selector') {
meta.selector = assertString(namedArg.expression).stringValue;
}
if (paramName == 'map') {
MapLiteral map = namedArg.expression;
map.entries.forEach((MapLiteralEntry entry) {
meta.attributeMappings[assertString(entry.key).stringValue] =
assertString(entry.value).stringValue;
});
}
if (paramName == 'exportExpressions') {
meta.exportExpressions = getStringValues(namedArg.expression);
}
if (paramName == 'exportExpressionAttrs') {
meta.exportExpressionAttrs = getStringValues(namedArg.expression);
}
}
});
});
});
}
}
List<String> getStringValues(ListLiteral listLiteral) {
List<String> res = <String>[];
for (Expression element in listLiteral.elements) {
res.add(assertString(element).stringValue);
}
return res;
}
StringLiteral assertString(Expression key) {
if (key is! StringLiteral) throw 'must be a string literal: ${key.runtimeType}';
return key;
}
part of angular.core;
/**
* Used by [Scope.$on] to notify the listeners of events.
*/
class ScopeEvent {
String name;
Scope targetScope;
Scope currentScope;
bool propagationStopped = false;
bool defaultPrevented = false;
ScopeEvent(this.name, this.targetScope);
stopPropagation () => propagationStopped = true;
preventDefault() => defaultPrevented = true;
}
/**
* Allows the configuration of [Scope.$digest] iteration maximum time-to-live
* value. Digest keeps checking the state of the watcher getters until it
* can execute one full iteration with no watchers triggering. TTL is used
* to prevent an infinite loop where watch A triggers watch B which in turn
* triggers watch A. If the system does not stabilize in TTL iteration then
* an digest is stop an an exception is thrown.
*/
class ScopeDigestTTL {
final num ttl;
ScopeDigestTTL(): ttl = 5;
ScopeDigestTTL.value(num this.ttl);
}
/**
* Scope has two responsibilities. 1) to keep track af watches and 2)
* to keep references to the model so that they are available for
* data-binding.
*/
@proxy
class Scope implements Map {
String $id;
Scope $parent;
Scope $root;
num _nextId = 0;
ExceptionHandler _exceptionHandler;
Parser _parser;
Zone _zone;
num _ttl;
String _phase;
Map<String, Object> _properties = {};
List<Function> _innerAsyncQueue;
List<Function> _outerAsyncQueue;
List<_Watch> _watchers = [];
Map<String, List<Function>> _listeners = {};
Scope _nextSibling, _prevSibling, _childHead, _childTail;
bool _isolate = false;
Profiler _perf;
Scope(ExceptionHandler this._exceptionHandler, Parser this._parser,
ScopeDigestTTL ttl, Zone this._zone, Profiler this._perf) {
_properties[r'this']= this;
_ttl = ttl.ttl;
$root = this;
$id = '_${$root._nextId++}';
_innerAsyncQueue = [];
_outerAsyncQueue = [];
// Set up the zone to auto digest this scope.
_zone.onTurnDone = $digest;
_zone.onError = (e, s, ls) => _exceptionHandler(e, s);
}
Scope._child(Scope this.$parent, bool this._isolate, Profiler this._perf) {
_exceptionHandler = $parent._exceptionHandler;
_parser = $parent._parser;
_ttl = $parent._ttl;
_properties[r'this'] = this;
_zone = $parent._zone;
$root = $parent.$root;
$id = '_${$root._nextId++}';
_innerAsyncQueue = $parent._innerAsyncQueue;
_outerAsyncQueue = $parent._outerAsyncQueue;
_prevSibling = $parent._childTail;
if ($parent._childHead != null) {
$parent._childTail._nextSibling = this;
$parent._childTail = this;
} else {
$parent._childHead = $parent._childTail = this;
}
}
_identical(a, b) =>
identical(a, b) ||
(a is String && b is String && a == b) ||
(a is num && b is num && a.isNaN && b.isNaN);
containsKey(String name) => this[name] != null;
remove(String name) => this._properties.remove(name);
operator []=(String name, value) => _properties[name] = value;
operator [](String name) {
if (name == r'$id') return this.$id;
if (name == r'$parent') return this.$parent;
if (name == r'$root') return this.$root;
var scope = this;
do {
if (scope._properties.containsKey(name)) {
return scope._properties[name];
} else if (!scope._isolate) {
scope = scope.$parent;
} else {
return null;
}
} while(scope != null);
return null;
}
noSuchMethod(Invocation invocation) {
var name = MirrorSystem.getName(invocation.memberName);
if (invocation.isGetter) {
return this[name];
} else if (invocation.isSetter) {
var value = invocation.positionalArguments[0];
name = name.substring(0, name.length - 1);
this[name] = value;
return value;
} else {
if (this[name] is Function) {
return this[name]();
} else {
super.noSuchMethod(invocation);
}
}
}
$new([bool isolate = false]) {
return new Scope._child(this, isolate, _perf);
}
$watch(watchExp, [Function listener, String watchStr]) {
if (watchStr == null) {
watchStr = watchExp.toString();
// print("CKCK: $watchStr");
// Keep prod fast
assert((() {
//ckck
if (watchExp is Function) {
watchStr = "${watchExp.hashCode}";
}
return true;
//ckck
if (watchExp is Function) {
watchStr = "FN: ${m.function.source}";
var m = reflect(watchExp);
if (m is ClosureMirror) {
// work-around dartbug.com/14130
try {
watchStr = "FN: ${m.function.source}";
} on NoSuchMethodError catch (e) {}
}
}
return true;
})());
}
var watcher = new _Watch(_compileToFn(listener), _initWatchVal,
_compileToFn(watchExp), watchStr);
// we use unshift since we use a while loop in $digest for speed.
// the while loop reads in reverse order.
_watchers.insert(0, watcher);
return () => _watchers.remove(watcher);
}
$watchCollection(obj, listener) {
var oldValue;
var newValue;
num changeDetected = 0;
Function objGetter = _compileToFn(obj);
List internalArray = [];
Map internalMap = {};
num oldLength = 0;
var $watchCollectionWatch = (_) {
newValue = objGetter(this);
var newLength, key;
if (newValue is! Map && newValue is! List) {
if (!_identical(oldValue, newValue)) {
oldValue = newValue;
changeDetected++;
}
} else if (newValue is List) {
if (!_identical(oldValue, internalArray)) {
// we are transitioning from something which was not an array into array.
oldValue = internalArray;
oldLength = oldValue.length = 0;
changeDetected++;
}
newLength = newValue.length;
if (oldLength != newLength) {
// if lengths do not match we need to trigger change notification
changeDetected++;
oldValue.length = oldLength = newLength;
}
// copy the items to oldValue and look for changes.
for (var i = 0; i < newLength; i++) {
if (!_identical(oldValue[i], newValue[i])) {
changeDetected++;
oldValue[i] = newValue[i];
}
}
} else { // Map
if (!_identical(oldValue, internalMap)) {
// we are transitioning from something which was not an object into object.
oldValue = internalMap = {};
oldLength = 0;
changeDetected++;
}
// copy the items to oldValue and look for changes.
newLength = 0;
newValue.forEach((key, value) {
newLength++;
if (oldValue.containsKey(key)) {
if (!_identical(oldValue[key], value)) {
changeDetected++;
oldValue[key] = value;
}
} else {
oldLength++;
oldValue[key] = value;
changeDetected++;
}
});
if (oldLength > newLength) {
// we used to have more keys, need to find them and destroy them.
changeDetected++;
var keysToRemove = [];
oldValue.forEach((key, _) {
if (!newValue.containsKey(key)) {
oldLength--;
keysToRemove.add(key);
}
});
keysToRemove.forEach((k) {
oldValue.remove(k);
});
}
}
return changeDetected;
};
var $watchCollectionAction = (_, __, ___) {
relaxFnApply(listener, [newValue, oldValue, this]);
};
return this.$watch($watchCollectionWatch, $watchCollectionAction);
}
/**
* Add this function to your code if you want to add a $digest
* and want to assert that the digest will be called on this turn.
* This method will be deleted when we are comfortable with
* auto-digesting scope.
*/
$$verifyDigestWillRun() {
_zone.assertInTurn();
}
$digest() => _perf.time('angular.scope.digest', () {
var innerAsyncQueue = _innerAsyncQueue,
length,
dirty, _ttlLeft = _ttl,
logIdx, logMsg;
List<List<String>> watchLog = [];
List<_Watch> watchers;
_Watch watch;
Scope next, current, target = this;
_beginPhase('\$digest');
try {
do { // "while dirty" loop
dirty = false;
current = target;
//asyncQueue = current._asyncQueue;
//dump('aQ: ${asyncQueue.length}');
while(innerAsyncQueue.length > 0) {
try {
$root.$eval(innerAsyncQueue.removeAt(0));
} catch (e, s) {
_exceptionHandler(e, s);
}
}
do { // "traverse the scopes" loop
if ((watchers = current._watchers) != null) {
// process our watches
length = watchers.length;
while (length-- > 0) {
try {
watch = watchers[length];
var value = watch.get(current);
var last = watch.last;
if (!_identical(value, last)) {
dirty = true;
watch.last = value;
watch.fn(value, ((last == _initWatchVal) ? value : last), current);
if (_ttlLeft < 5) {
logIdx = 4 - _ttlLeft;
while (watchLog.length <= logIdx) {
watchLog.add([]);
}
logMsg = (watch.exp is Function)
? 'fn: ' + (watch.exp.toString())
: watch.exp;
logMsg += '; newVal: ' + _toJson(value) + '; oldVal: ' + _toJson(last);
watchLog[logIdx].add(logMsg);
}
}
} catch (e, s) {
_exceptionHandler(e, s);
}
}
}
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $broadcast
if (current._childHead == null) {
if (current == target) {
next = null;
} else {
next = current._nextSibling;
if (next == null) {
while(current != target && (next = current._nextSibling) == null) {
current = current.$parent;
}
}
}
} else {
next = current._childHead;
}
} while ((current = next) != null);
if(dirty && (_ttlLeft--) == 0) {
throw '$_ttl \$digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: ${_toJson(watchLog)}';
}
} while (dirty || innerAsyncQueue.length > 0);
while(_outerAsyncQueue.length > 0) {
try {
$root.$eval(_outerAsyncQueue.removeAt(0));
} catch (e, s) {
_exceptionHandler(e, s);
}
}
} finally {
_clearPhase();
}
});
$destroy() {
if ($root == this) return; // we can't remove the root node;
$broadcast(r'$destroy');
if ($parent._childHead == this) $parent._childHead = _nextSibling;
if ($parent._childTail == this) $parent._childTail = _prevSibling;
if (_prevSibling != null) _prevSibling._nextSibling = _nextSibling;
if (_nextSibling != null) _nextSibling._prevSibling = _prevSibling;
}
$eval(expr, [locals]) {
return relaxFnArgs(_compileToFn(expr))(this, locals);
}
$evalAsync(expr, {outsideDigest: false}) {
if (outsideDigest) {
_outerAsyncQueue.add(expr);
} else {
_innerAsyncQueue.add(expr);
}
}
$apply([expr]) {
return _zone.run(() {
try {
return $eval(expr);
} catch (e, s) {
_exceptionHandler(e, s);
}
});
}
$on(name, listener) {
var namedListeners = _listeners[name];
if (!_listeners.containsKey(name)) {
_listeners[name] = namedListeners = [];
}
namedListeners.add(listener);
return () {
namedListeners.remove(listener);
};
}
$emit(name, [List args]) {
var empty = [],
namedListeners,
scope = this,
event = new ScopeEvent(name, this),
listenerArgs = [event],
i;
if (args != null) {
listenerArgs.addAll(args);
}
do {
namedListeners = scope._listeners[name];
if (namedListeners != null) {
event.currentScope = scope;
i = 0;
for (var length = namedListeners.length; i<length; i++) {
try {
relaxFnApply(namedListeners[i], listenerArgs);
if (event.propagationStopped) return event;
} catch (e, s) {
_exceptionHandler(e, s);
}
}
}
//traverse upwards
scope = scope.$parent;
} while (scope != null);
return event;
}
$broadcast(String name, [List listenerArgs]) {
var target = this,
current = target,
next = target,
event = new ScopeEvent(name, this);
//down while you can, then up and next sibling or up and next sibling until back at root
if (listenerArgs == null) {
listenerArgs = [];
}
listenerArgs.insert(0, event);
do {
current = next;
event.currentScope = current;
if (current._listeners.containsKey(name)) {
current._listeners[name].forEach((listener) {
try {
relaxFnApply(listener, listenerArgs);
} catch(e, s) {
_exceptionHandler(e, s);
}
});
}
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $broadcast
if (current._childHead == null) {
if (current == target) {
next = null;
} else {
next = current._nextSibling;
if (next == null) {
while(current != target && (next = current._nextSibling) == null) {
current = current.$parent;
}
}
}
} else {
next = current._childHead;
}
} while ((current = next) != null);
return event;
}
_beginPhase(phase) {
if ($root._phase != null) {
// TODO(deboer): Remove the []s when dartbug.com/11999 is fixed.
throw ['${$root._phase} already in progress'];
}
$root._phase = phase;
}
_clearPhase() {
$root._phase = null;
}
Function _compileToFn(exp) {
if (exp == null) {
return () => null;
} else if (exp is String) {
return _parser(exp).eval;
} else if (exp is Function) {
return exp;
} else {
throw 'Expecting String or Function';
}
}
}
var _initWatchVal = new Object();
class _Watch {
Function fn;
dynamic last;
Function get;
String exp;
_Watch(fn, this.last, getFn, this.exp) {
this.fn = relaxFnArgs3(fn);
this.get = relaxFnArgs1(getFn);
}
}
_toJson(obj) {
try {
return stringify(obj);
} catch(e) {
var ret = "NOT-JSONABLE";
// Keep prod fast.
assert((() {
var mirror = reflect(obj);
if (mirror is ClosureMirror) {
// work-around dartbug.com/14130
try {
ret = mirror.function.source;
} on NoSuchMethodError catch (e) {}
}
return true;
})());
return ret;
}
}
library angular.playback.playback_data;
// During HTTP playback, this file will be replaced with a file
// that has playback data.
Map playbackData = { };
library dart_code_gen_source;
import 'dart_code_gen.dart';
class SourceBuilder {
static RegExp NON_WORDS = new RegExp(r'\W');
Map<String, Code> refs = {};
List<Code> codeRefs = [];
String str(String s) => '\'' + s.replaceAll('\'', '\\\'').replaceAll(r'$', r'\$') + '\'';
String ident(String s) => '_${s.replaceAll(NON_WORDS, '_')}_${s.hashCode}';
String ref(Code code) {
if (!refs.containsKey(code.id)) {
refs[code.id] = code;
code.toSource(this); // recursively expand;
codeRefs.add(code);
}
return this.ident(code.id);
}
parens([p1, p2, p3, p4, p5, p6]) => new ParenthesisSource()..call(p1, p2, p3, p4, p5, p6);
body([p1, p2, p3, p4, p5, p6]) => new BodySource()..call(p1, p2, p3, p4, p5, p6);
stmt([p1, p2, p3, p4, p5, p6]) => new StatementSource()..call(p1, p2, p3, p4, p5, p6);
call([p1, p2, p3, p4, p5, p6]) => new Source()..call(p1, p2, p3, p4, p5, p6);
}
class Source {
static String NEW_LINE = '\n';
List source = [];
call([p1, p2, p3, p4, p5, p6]) {
if (p1 != null) source.add(p1);
if (p2 != null) source.add(p2);
if (p3 != null) source.add(p3);
if (p4 != null) source.add(p4);
if (p5 != null) source.add(p5);
if (p6 != null) source.add(p6);
}
toString([String indent='', newLine=false, sep='']) {
var lines = [];
var trailing = sep == ';';
var _sep = '';
source.forEach((s) {
if (!trailing) lines.add(_sep);
if (newLine) lines.add('\n' + indent);
if (s is Source) {
lines.add(s.toString(indent));
} else {
lines.add(s);
}
_sep = sep;
if (trailing) lines.add(_sep);
});
return lines.join('');
}
}
class ParenthesisSource extends Source {
toString([String indent='']) {
return '(' + super.toString(indent + ' ', true, ',') + ')';
}
}
class MapSource extends Source {
toString([String indent='']) {
return '{' + super.toString(indent + ' ', true, ',') + '}';
}
}
class BodySource extends Source {
BodySource() {
//this('');
}
toString([String indent='']) {
return '{' + super.toString(indent + ' ', true) + '\n$indent}';
}
}
class StatementSource extends Source {
toString([String indent='']) {
return '${super.toString(indent + ' ')};';
}
}
library angular.perf;
import 'package:di/di.dart';
import 'package:perf_api/perf_api.dart';
class NgPerfModule extends Module {
NgPerfModule() {
type(Profiler);
}
}
part of angular.directive;
typedef dynamic ItemEval(dynamic item, num index);
/**
* HTML [SELECT] element with angular data-binding if used with [NgModelDirective].
*
* The [NgModelDirective] will receive the currently selected item. The binding is
* performed on the [OPTION].[value] property.
* An empty [OPTION].[value] is treated as null.
*
* If you the model contains value which does not map to any [OPTION] then a new
* unknown [OPTION] is inserted into the list. Once the model points to an existing
* [OPTION] the unknown [OPTION] is removed.
*
* Becouse [OPTION].[value] attribute is a string, the model is bound to a string.
* If there is need to bind to an object then [OptionValueDirective] should be used.
*
*/
@NgDirective(
selector: 'select[ng-model]',
visibility: NgDirective.CHILDREN_VISIBILITY
)
class InputSelectDirective implements NgAttachAware {
final Expando<OptionValueDirective> expando = new Expando<OptionValueDirective>();
final dom.SelectElement _selectElement;
final NodeAttrs _attrs;
final NgModel _model;
final Scope _scope;
final dom.OptionElement _unknownOption = new dom.OptionElement();
dom.OptionElement _nullOption;
_SelectMode _mode = new _SelectMode(null, null, null);
bool _dirty = false;
InputSelectDirective(dom.Element this._selectElement,
NodeAttrs this._attrs,
NgModel this._model,
Scope this._scope) {
_unknownOption.value = '?';
_selectElement.queryAll('option').forEach((o) {
if (_nullOption == null && o.value == '') {
_nullOption = o;
}
});
}
attach() {
_attrs.observe('multiple', (value) {
_mode.destroy();
if (value == null) {
_model.watchCollection = false;
_mode = new _SingleSelectMode(expando, _selectElement, _model, _nullOption, _unknownOption);
} else {
_model.watchCollection = true;
_mode = new _MultipleSelectionMode(expando, _selectElement, _model);
}
_mode.onModelChange(_model.viewValue);
});
_selectElement.onChange.listen((event) => _mode.onViewChange(event));
_model.render = (value) => _mode.onModelChange(value);
}
/**
* This method invalidates the current state of the selector and forces a rerendering of the
* options using the [Scope.$evalAsync].
*/
dirty() {
if (!_dirty) {
_dirty = true;
_scope.$evalAsync(() {
_dirty = false;
_mode.onModelChange(_model.viewValue);
});
}
}
}
/**
* Since the [value] attirbute of the [OPTION] can only be a string, Angular provides
* [ng-value] which allows binding to any expression.
*
*/
@NgDirective(
selector: 'option',
publishTypes: const [TextChangeListener],
map: const {'ng-value': '&.ngValue'}
)
class OptionValueDirective implements TextChangeListener, NgAttachAware, NgDetachAware {
final InputSelectDirective _inputSelectDirective;
final NodeAttrs _attrs;
Getter _ngValue;
OptionValueDirective(NodeAttrs this._attrs,
InputSelectDirective this._inputSelectDirective) {
if (_inputSelectDirective != null) {
_inputSelectDirective.expando[_attrs.element] = this;
}
}
attach() {
if (_inputSelectDirective != null) {
this._attrs.observe('value', (_) => _inputSelectDirective.dirty());
}
}
call(String text) {
if (_inputSelectDirective != null) {
_inputSelectDirective.dirty();
}
}
detach() {
if (_inputSelectDirective != null) {
_inputSelectDirective.dirty();
_inputSelectDirective.expando[_attrs.element] = null;
}
}
set ngValue(Getter value) => _ngValue = value;
get ngValue {
return _attrs['ng-value'] is String ? _ngValue() : (_attrs.element as dom.OptionElement).value;
}
}
class _SelectMode {
final Expando<OptionValueDirective> expando;
final dom.SelectElement select;
final NgModel model;
_SelectMode(Expando<OptionValueDirective> this.expando,
dom.SelectElement this.select,
NgModel this.model);
onViewChange(event) {}
onModelChange(value) {}
destroy() {}
get _options => select.queryAll('option');
_forEachOption(fn, [quiteOnReturn = false]) {
for(var os = _options, i = 0, ii = os.length; i < ii; i++) {
var retValue = fn(os[i], i);
if (quiteOnReturn && retValue != null) {
return retValue;
}
}
return null;
}
}
class _SingleSelectMode extends _SelectMode {
final dom.OptionElement _unknownOption;
final dom.OptionElement _nullOption;
bool _unknownOptionActive = false;
_SingleSelectMode(Expando<OptionValueDirective> expando,
dom.SelectElement select,
NgModel model,
dom.OptionElement this._nullOption,
dom.OptionElement this._unknownOption
): super(expando, select, model) {
}
onViewChange(event) {
var i = 0;
model.viewValue = _forEachOption((option, _) {
if (option.selected) {
return option == _nullOption ? null : expando[option].ngValue;
}
if (option != _unknownOption && option != _nullOption) {
i++;
}
}, true);
}
onModelChange(value) {
var found = false;
_forEachOption((option, i) {
if (option == _unknownOption) return;
var selected = value == null ? option == _nullOption : expando[option].ngValue == value;
found = found || selected;
option.selected = selected;
});
if (!found) {
if (!_unknownOptionActive) {
select.insertBefore(_unknownOption, select.firstChild);
_unknownOption.selected = true;
_unknownOptionActive = true;
}
} else {
if (_unknownOptionActive) {
_unknownOption.remove();
_unknownOptionActive = false;
}
}
}
}
class _MultipleSelectionMode extends _SelectMode {
_MultipleSelectionMode(Expando<OptionValueDirective> expando,
dom.SelectElement select,
NgModel model
): super(expando, select, model);
onViewChange(event) {
var selected = [];
var fn = (o, i) => o.value;
_forEachOption((o, i) {
if (o.selected) {
selected.add(expando[o].ngValue);
}
});
model.viewValue = selected;
}
onModelChange(List selectedValues) {
num index = 0;
Function fn = (o, i) => o.selected = null;
if (selectedValues is List) {
fn = (o, i) => o.selected = selectedValues.contains(expando[o].ngValue);
}
_forEachOption(fn);
}
}
library generator;
import 'dart_code_gen.dart';
import '../../core/parser/parser_library.dart';
import 'source.dart';
class ParserGenerator {
DynamicParser _parser;
Map<String, boolean> _printedFunctions = {};
GetterSetterGenerator _getters;
SourceBuilder _ = new SourceBuilder();
ParserGenerator(DynamicParser this._parser,
GetterSetterGenerator this._getters);
generateParser(List<String> expressions) {
print("genEvalError(msg) { throw msg; }");
print("functions(FilterLookup filters) => new StaticParserFunctions(buildExpressions(filters));");
print('var evalError = (text, [s]) => text;');
print("");
BodySource body = new BodySource();
MapSource map = new MapSource();
// deterimine the order.
expressions.forEach((exp) {
var code = safeCode(exp);
map('${_.str(exp)}: ${_.ref(code)}');
});
// now do it in actual order
_.codeRefs.forEach((code) {
body(_.stmt('Expression ${_.ref(code)} = ', code.toSource(_)));
});
body(_.stmt('return ', map));
print("Map<String, Expression> buildExpressions(FilterLookup filters) ${body}");
print("\n");
print(_getters.functions);
}
String generateDart(String expression) {
var tokens = _lexer(expression);
}
Code safeCode(String exp) {
try {
return _parser(exp);
} catch (e) {
if ("$e".contains('Parser Error') ||
"$e".contains('Lexer Error') ||
"$e".contains('Unexpected end of expression')) {
return new ThrowCode("'${escape(e.toString())}';");
} else {
rethrow;
}
}
}
}
part of angular.core.dom;
/**
* Infinite cache service for templates loaded from URLs.
*
* All templates that are loaded from a URL are cached indefinitely in the
* TemplateCache the first time they are needed. This includes templates loaded
* via `ng-include` or via the `templateUrl` field on components decorated with
* [NgComponent].
*
* All attempts that require loading a template from a URL are first checked
* against this cache. Only when there is a cache miss is a network request
* attempted.
*
* You are welcome to pre-load / seed the TemplateCache with templates for URLs
* in advance to avoid the network hit on first load.
*
* There are two ways to seed the TemplateCache – (1) imperatively via and
* `TemplateCache` service or (2) declaratively in HTML via the `<template>`
* element (handled by [NgTemplateElementDirective]).
*
* Here is an example that illustrates both techniques
* ([view in plunker](http://plnkr.co/edit/JCsxhH?p=info)):
*
* Example:
*
* // main.dart
* import 'package:angular/angular.dart';
*
* @NgDirective(selector: '[main-controller]')
* class LoadTemplateCacheDirective {
* LoadTemplateCacheDirective(TemplateCache templateCache, Scope scope) {
* // Method 1 (imperative): Via the injected TemplateCache service.
* templateCache.put(
* 'template_1.html', new HttpResponse(200, 't1: My name is {{name}}.'));
* scope.name = "chirayu";
* }
* }
*
* main() {
* bootstrapAngular([new AngularModule()..type(LoadTemplateCacheDirective)], 'html');
* }
*
* and
*
* <!-- index.html -->
* <html>
* <head>
* <script src="packages/browser/dart.js"></script>
* <script src="main.dart" type="application/dart"></script>
*
* <!-- Method 2 (declarative): Via the template directive. -->
* <template id="template_2.html" type="text/ng-template">
* t2: My name is {{name}}.
* </template>
* </head>
* <body load-template-cache>
* template_1.html: <div ng-include="'template_1.html'"></div><br>
* template_2.html: <div ng-include="'template_2.html'"></div><br>
* </body>
* </html>
*
* Neither `ng-include` above will result in a network hit. This means that it
* isn't necessary for your webserver to even serve those templates.
*
* `template_1.html` is preloaded into the [TemplateCache] imperatively by
* `LoadTemplateCacheDirective` while `template_2.html` is preloaded via the
* `<template id="template_2.html" type="text/ng-template">` element
* declaratively in the `<head>` of HTML.
*/
class TemplateCache extends LruCache<String, HttpResponse> {
TemplateCache({int capacity}): super(capacity: capacity);
}
library di.injector;
import 'module.dart';
abstract class Injector {
/**
* Returns the name of the injector or null of none is given.
*/
String get name;
/**
* Returns the parent injector or null if root.
*/
Injector get parent;
/**
* Get an instance for given token ([Type]).
*
* If the injector already has an instance for this token, it returns this
* instance. Otherwise, injector resolves all its dependencies, instantiate
* new instance and returns this instance.
*
* If there is no binding for given token, injector asks parent injector.
*
* If there is no parent injector, an implicit binding is used. That is,
* the token ([Type]) is instantiated.
*/
dynamic get(Type type);
/**
* Return a list of all types which the injector can return
*/
Set<Type> get types;
/**
* Create a child injector.
*
* Child injector can override any bindings by adding additional modules.
*
* It also accepts a list of tokens that a new instance should be forced.
* That means, even if some parent injector already has an instance for this
* token, there will be a new instance created in the child injector.
*/
Injector createChild(List<Module> modules, {List<Type> forceNewInstances,
String name});
}
part of angular.core;
class CacheStats {
final int capacity;
final int size;
final int hits;
final int misses;
CacheStats(int this.capacity, int this.size, int this.hits, int this.misses);
String toString() =>
"[CacheStats: capacity: $capacity, size: $size, hits: $hits, misses: $misses]";
}
/**
* The Cache interface.
*/
abstract class Cache<K, V> {
/**
* Returns the value for `key` from the cache. If `key` is not in the cache,
* returns `null`.
*/
V get(K key);
/**
* Inserts/Updates the `key` in the cache with `value` and returns the value.
*/
V put(K key, V value);
/**
* Removes `key` from the cache. If `key` isn't present in the cache, does
* nothing.
*/
V remove(K key);
/**
* Removes all entries from the cache.
*/
void removeAll();
int get capacity;
int get size;
CacheStats stats();
}
/**
* An unbounded cache.
*/
class UnboundedCache<K, V> implements Cache<K, V> {
Map<K, V> _entries = <K, V>{};
int _hits = 0;
int _misses = 0;
V get(K key) {
V value = _entries[key];
if (value != null || _entries.containsKey(key)) {
++_hits;
} else {
++_misses;
}
return value;
}
V put(K key, V value) => _entries[key] = value;
V remove(K key) => _entries.remove(key);
void removeAll() => _entries.clear();
int get capacity => 0;
int get size => _entries.length;
CacheStats stats() => new CacheStats(capacity, size, _hits, _misses);
// Debugging helper.
String toString() => "[$runtimeType: size=${_entries.length}, items=$_entries]";
}
/**
* Simple LRU cache.
*
* TODO(chirayu):
* - add docs
* - add tests
* - should stringify keys?
*/
class LruCache<K, V> extends Cache<K, V> {
Map<K, V> _entries = new LinkedHashMap<K, V>();
int _capacity;
int _hits = 0;
int _misses = 0;
LruCache({int capacity}) {
this._capacity = (capacity == null) ? 0 : capacity;
}
V get(K key) {
V value = _entries[key];
if (value != null || _entries.containsKey(key)) {
++_hits;
// refresh
_entries.remove(key);
_entries[key] = value;
} else {
++_misses;
}
return value;
}
V put(K key, V value) {
// idempotent. needed to refresh an existing key.
_entries.remove(key);
// _capacity always > 0 but might not be true in some future.
if (_capacity > 0 && _capacity == _entries.length) {
// drop oldest entry when at capacity
// _entries.keys.first is fairly cheap - 2 new calls.
_entries.remove(_entries.keys.first);
}
_entries[key] = value;
return value;
}
V remove(K key) => _entries.remove(key);
void removeAll() => _entries.clear();
int get capacity => _capacity;
int get size => _entries.length;
CacheStats stats() => new CacheStats(capacity, size, _hits, _misses);
// Debugging helper.
String toString() => "[$runtimeType: capacity=$capacity, size=$size, items=$_entries]";
}
part of angular.core.dom;
/**
* DirectiveSelector is a function which given a node it will return a
* list of [DirectiveRef]s which are triggered by this node.
*
* DirectiveSelector is used by the [Compiler] during the template walking
* to extract the [DirectiveRef]s.
*
* DirectiveSelector can be created using the [directiveSelectorFactory]
* method.
*
* The DirectiveSelector supports CSS selectors which do not cross
* element boundaries only. The selectors can have any mix of element-name,
* class-names and attribute-names.
*
* Examples:
*
* <pre>
* element
* .class
* [attribute]
* [attribute=value]
* element[attribute1][attribute2=value]
* </pre>
*
*
*/
typedef List<DirectiveRef> DirectiveSelector(dom.Node node);
class _Directive {
final Type type;
final NgAnnotation annotation;
_Directive(Type this.type, NgAnnotation this.annotation);
}
class _ContainsSelector {
NgAnnotation annotation;
RegExp regexp;
_ContainsSelector(NgAnnotation this.annotation, String regexp) {
this.regexp = new RegExp(regexp);
}
}
RegExp _SELECTOR_REGEXP = new RegExp(r'^(?:([\w\-]+)|(?:\.([\w\-]+))|(?:\[([\w\-]+)(?:=([^\]]*))?\]))');
RegExp _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([\w\-]+)(?:\=(.*))?\]$');
RegExp _CONTAINS_REGEXP = new RegExp(r'^:contains\(\/(.+)\/\)$'); //
RegExp _ATTR_CONTAINS_REGEXP = new RegExp(r'^\[\*=\/(.+)\/\]$'); //
class _SelectorPart {
final String element;
final String className;
final String attrName;
final String attrValue;
const _SelectorPart.fromElement(String this.element)
: className = null, attrName = null, attrValue = null;
const _SelectorPart.fromClass(String this.className)
: element = null, attrName = null, attrValue = null;
const _SelectorPart.fromAttribute(String this.attrName, String this.attrValue)
: element = null, className = null;
toString() =>
element == null
? (className == null
? (attrValue == '' ? '[$attrName]' : '[$attrName=$attrValue]')
: '.$className')
: element;
}
class _ElementSelector {
String name;
Map<String, _Directive> elementMap = new Map<String, _Directive>();
Map<String, _ElementSelector> elementPartialMap = new Map<String, _ElementSelector>();
Map<String, _Directive> classMap = new Map<String, _Directive>();
Map<String, _ElementSelector> classPartialMap = new Map<String, _ElementSelector>();
Map<String, Map<String, _Directive>> attrValueMap = new Map<String, Map<String, _Directive>>();
Map<String, Map<String, _ElementSelector>> attrValuePartialMap = new Map<String, Map<String, _ElementSelector>>();
_ElementSelector(String this.name);
addDirective(List<_SelectorPart> selectorParts, _Directive directive) {
var selectorPart = selectorParts.removeAt(0);
var terminal = selectorParts.isEmpty;
var name;
if ((name = selectorPart.element) != null) {
if (terminal) {
elementMap[name] = directive;
} else {
elementPartialMap
.putIfAbsent(name, () => new _ElementSelector(name))
.addDirective(selectorParts, directive);
}
} else if ((name = selectorPart.className) != null) {
if (terminal) {
classMap[name] = directive;
} else {
classPartialMap
.putIfAbsent(name, () => new _ElementSelector(name))
.addDirective(selectorParts, directive);
}
} else if ((name = selectorPart.attrName) != null) {
if (terminal) {
attrValueMap
.putIfAbsent(name, () => new Map<String, _Directive>())
[selectorPart.attrValue] = directive;
} else {
attrValuePartialMap
.putIfAbsent(name, () => new Map<String, _ElementSelector>())
[selectorPart.attrValue] = new _ElementSelector(name)
..addDirective(selectorParts, directive);
}
} else {
throw "Unknown selector part '$selectorPart'.";
}
}
List<_ElementSelector> selectNode(List<DirectiveRef> refs, List<_ElementSelector> partialSelection,
dom.Node node, String nodeName) {
if (elementMap.containsKey(nodeName)) {
_Directive directive = elementMap[nodeName];
refs.add(new DirectiveRef(node, directive.type, directive.annotation));
}
if (elementPartialMap.containsKey(nodeName)) {
if (partialSelection == null) partialSelection = new List<_ElementSelector>();
partialSelection.add(elementPartialMap[nodeName]);
}
return partialSelection;
}
List<_ElementSelector> selectClass(List<DirectiveRef> refs, List<_ElementSelector> partialSelection,
dom.Node node, String className) {
if (classMap.containsKey(className)) {
var directive = classMap[className];
refs.add(new DirectiveRef(node, directive.type, directive.annotation));
}
if (classPartialMap.containsKey(className)) {
if (partialSelection == null) partialSelection = new List<_ElementSelector>();
partialSelection.add(classPartialMap[className]);
}
return partialSelection;
}
List<_ElementSelector> selectAttr(List<DirectiveRef> refs, List<_ElementSelector> partialSelection,
dom.Node node, String attrName, String attrValue) {
if (attrValueMap.containsKey(attrName)) {
Map<String, _Directive> valuesMap = attrValueMap[attrName];
if (valuesMap.containsKey('')) {
_Directive directive = valuesMap[''];
refs.add(new DirectiveRef(node, directive.type, directive.annotation, attrValue));
}
if (attrValue != '' && valuesMap.containsKey(attrValue)) {
_Directive directive = valuesMap[attrValue];
refs.add(new DirectiveRef(node, directive.type, directive.annotation, attrValue));
}
}
if (attrValuePartialMap.containsKey(attrName)) {
Map<String, _ElementSelector> valuesPartialMap = attrValuePartialMap[attrName];
if (valuesPartialMap.containsKey('')) {
if (partialSelection == null) partialSelection = new List<_ElementSelector>();
partialSelection.add(valuesPartialMap['']);
}
if (attrValue != '' && valuesPartialMap.containsKey(attrValue)) {
if (partialSelection == null) partialSelection = new List<_ElementSelector>();
partialSelection.add(valuesPartialMap[attrValue]);
}
}
return partialSelection;
}
toString() => 'ElementSelector($name)';
}
List<_SelectorPart> _splitCss(String selector) {
List<_SelectorPart> parts = [];
var remainder = selector;
var match;
while (!remainder.isEmpty) {
if ((match = _SELECTOR_REGEXP.firstMatch(remainder)) != null) {
if (match[1] != null) {
parts.add(new _SelectorPart.fromElement(match[1].toLowerCase()));
} else if (match[2] != null) {
parts.add(new _SelectorPart.fromClass(match[2].toLowerCase()));
} else if (match[3] != null) {
var attrValue = match[4] == null ? '' : match[4].toLowerCase();
parts.add(new _SelectorPart.fromAttribute(match[3].toLowerCase(),
attrValue));
} else {
throw "Missmatched RegExp $_SELECTOR_REGEXP on $remainder";
}
} else {
throw "Unknown selector format '$remainder'.";
}
remainder = remainder.substring(match.end);
}
return parts;
}
/**
*
* Factory method for creating a [DirectiveSelector].
*/
DirectiveSelector directiveSelectorFactory(DirectiveMap directives) {
_ElementSelector elementSelector = new _ElementSelector('');
List<_ContainsSelector> attrSelector = [];
List<_ContainsSelector> textSelector = [];
directives.forEach((NgAnnotation annotation, Type type) {
var match;
var selector = annotation.selector;
List<_SelectorPart> selectorParts;
if ((match = _CONTAINS_REGEXP.firstMatch(selector)) != null) {
textSelector.add(new _ContainsSelector(annotation, match.group(1)));
} else if ((match = _ATTR_CONTAINS_REGEXP.firstMatch(selector)) != null) {
attrSelector.add(new _ContainsSelector(annotation, match[1]));
} else if ((selectorParts = _splitCss(selector)) != null){
elementSelector.addDirective(selectorParts, new _Directive(type, annotation));
} else {
throw new ArgumentError('Unsupported Selector: $selector');
}
});
return (dom.Node node) {
List<DirectiveRef> directiveRefs = [];
List<_ElementSelector> partialSelection = null;
Map<String, bool> classes = new Map<String, bool>();
Map<String, String> attrs = new Map<String, String>();
switch(node.nodeType) {
case 1: // Element
dom.Element element = node;
String nodeName = element.tagName.toLowerCase();
Map<String, String> attrs = {};
// Select node
partialSelection = elementSelector.selectNode(directiveRefs, partialSelection, element, nodeName);
// Select .name
if ((element.classes) != null) {
for(var name in element.classes) {
classes[name] = true;
partialSelection = elementSelector.selectClass(directiveRefs, partialSelection, element, name);
}
}
// Select [attributes]
element.attributes.forEach((attrName, value){
attrs[attrName] = value;
for(var k = 0, kk = attrSelector.length; k < kk; k++) {
_ContainsSelector selectorRegExp = attrSelector[k];
if (selectorRegExp.regexp.hasMatch(value)) {
// this directive is matched on any attribute name, and so
// we need to pass the name to the directive by prefixing it to the
// value. Yes it is a bit of a hack.
Type type = directives[selectorRegExp.annotation];
directiveRefs.add(new DirectiveRef(
node, type, selectorRegExp.annotation, '$attrName=$value'));
}
}
partialSelection = elementSelector.selectAttr(directiveRefs, partialSelection, node, attrName, value);
});
while(partialSelection != null) {
List<_ElementSelector> elementSelectors = partialSelection;
partialSelection = null;
elementSelectors.forEach((_ElementSelector elementSelector) {
classes.forEach((className, _) {
partialSelection = elementSelector.selectClass(directiveRefs, partialSelection, node, className);
});
attrs.forEach((attrName, value) {
partialSelection = elementSelector.selectAttr(directiveRefs, partialSelection, node, attrName, value);
});
});
}
break;
case 3: // Text Node
for(var value = node.nodeValue, k = 0, kk = textSelector.length; k < kk; k++) {
var selectorRegExp = textSelector[k];
if (selectorRegExp.regexp.hasMatch(value)) {
Type type = directives[selectorRegExp.annotation];
directiveRefs.add(new DirectiveRef(node, type, selectorRegExp.annotation, value));
}
}
break;
}
directiveRefs.sort(_priorityComparator);
return directiveRefs;
};
}
int _directivePriority(NgAnnotation annotation) {
if (annotation is NgDirective) {
return (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) ? 2 : 1;
} else if (annotation is NgComponent) {
return 0;
}
throw "Unexpected Type: ${annotation}.";
}
int _priorityComparator(DirectiveRef a, DirectiveRef b) {
return _directivePriority(b.annotation) - _directivePriority(a.annotation);
}
library di.module;
import 'dart:collection';
import 'injector.dart';
typedef dynamic FactoryFn(Injector injector);
/**
* Creation strategy is asked to return an instance of the type after
* [Injector.get] locates the defining injector that has no instance cached.
* [directInstantation] is true when an instance is created directly from
* [Injector.instantiate].
*/
typedef dynamic CreationStrategy(
Injector requesting,
Injector defining,
dynamic factory()
);
/**
* Visibility determines if the instance in the defining module is visible to
* the requesting injector. If true is returned, then the instance from the
* defining injector is provided. If false is returned, the injector keeps
* walking up the tree to find another visible instance.
*/
typedef bool Visibility(Injector requesting, Injector defining);
/**
* A collection of type bindings. Once the module is passed into the injector,
* the injector creates a copy of the module and all subsequent changes to the
* module have no effect.
*/
class Module {
final Map<Type, Binding> _bindings = new HashMap<Type, Binding>();
final List<Module> _childModules = <Module>[];
/**
* Compiles and returs bindings map by performing depth-first traversal of the
* child (installed) modules.
*/
Map<Type, Binding> get bindings {
Map<Type, Binding> res = new HashMap<Type, Binding>();
_childModules.forEach((child) => res.addAll(child.bindings));
res.addAll(_bindings);
return res;
}
/**
* Register binding to a concrete value.
*
* The [value] is what actually will be injected.
*/
void value(Type id, value,
{CreationStrategy creation, Visibility visibility}) {
_bindings[id] = new ValueBinding(value, creation, visibility);
}
/**
* Register binding to a [Type].
*
* The [implementedBy] will be instantiated using [new] operator and the
* resulting instance will be injected. If no type is provided, then it's
* implied that [id] should be instantiated.
*/
void type(Type id, {Type implementedBy, CreationStrategy creation,
Visibility visibility}) {
_bindings[id] = new TypeBinding(implementedBy == null ? id : implementedBy,
creation, visibility);
}
/**
* Register binding to a factory function.abstract
*
* The [factoryFn] will be called and all its arguments will get injected.
* The result of that function is the value that will be injected.
*/
void factory(Type id, FactoryFn factoryFn,
{CreationStrategy creation, Visibility visibility}) {
_bindings[id] = new FactoryBinding(factoryFn, creation, visibility);
}
/**
* Installs another module into this module. Bindings defined on this module
* take precidence over the installed module.
*/
void install(Module module) => _childModules.add(module);
}
/** Deafault creation strategy is to instantiate on the defining injector. */
dynamic _defaultCreationStrategy(Injector requesting, Injector defining,
dynamic factory()) => factory();
/** By default all values are visible to child injectors. */
bool _defaultVisibility(_, __) => true;
abstract class Binding {
final CreationStrategy creationStrategy;
final Visibility visibility;
Binding(_creationStrategy, _visibility)
: creationStrategy = _creationStrategy == null ?
_defaultCreationStrategy : _creationStrategy,
visibility = _visibility == null ?
_defaultVisibility : _visibility;
}
class ValueBinding extends Binding {
final Object value;
ValueBinding(this.value, [CreationStrategy creationStrategy,
Visibility visibility])
: super(creationStrategy, visibility);
}
class TypeBinding extends Binding {
final Type type;
TypeBinding(this.type, [CreationStrategy creationStrategy,
Visibility visibility])
: super(creationStrategy, visibility);
}
class FactoryBinding extends Binding {
final FactoryFn factoryFn;
FactoryBinding(this.factoryFn, [CreationStrategy creationStrategy,
Visibility visibility])
: super(creationStrategy, visibility);
}
library angular.mock;
import 'dart:async';
import 'dart:html';
import 'dart:json' as json;
import 'dart:mirrors' as mirror;
import 'dart:async' as dartAsync;
import '../angular.dart';
import '../utils.dart' as utils;
import 'package:js/js.dart' as js;
import 'package:di/di.dart';
part 'debug.dart';
part 'exception_handler.dart';
part 'http_backend.dart';
part 'log.dart';
part 'probe.dart';
part 'test_bed.dart';
part 'zone.dart';
/**
* Use in addition to [AngularModule] in your tests.
*
* [AngularMockModule] provides:
*
* - [TestBed]
* - [Probe]
* - [MockHttpBackend] instead of [HttpBackend]
* - [Logger]
* - [RethrowExceptionHandler] instead of [ExceptionHandler]
* - [ng.Zone] which displays errors to console;
*/
class AngularMockModule extends Module {
AngularMockModule() {
type(ExceptionHandler, implementedBy: RethrowExceptionHandler);
type(TestBed);
type(Probe);
type(Logger);
type(MockHttpBackend);
factory(HttpBackend, (Injector i) => i.get(MockHttpBackend));
factory(Zone, (_) {
Zone zone = new Zone();
zone.onError = (dynamic e, dynamic s, LongStackTrace ls) => dump('EXCEPTION: $e\n$s\n$ls');
return zone;
});
}
}
part of angular.filter;
typedef dynamic Mapper(dynamic e);
/**
* Orders the provided [Iterable] by the `expression` predicate.
*
* Example 1: Simple array and single/empty expression.
*
* Assume that you have an array on scope called `colors` and that it has a list
* of these strings – `['red', 'blue', 'green']`. You might sort these in
* ascending order this way:
*
* Colors: <ul>
* <li ng-repeat="color in colors | orderBy:''">{{color}}</li>
* </ul>
*
* That would result in:
*
* <ul>
* <li>blue</li>
* <li>green</li>
* <li>red</li>
* <ul>
*
* The empty string expression, `''`, here signifies sorting in ascending order
* using the default comparator. Using `'+'` would also work as the `+` prefix
* is implied.
*
* To sort in descending order, you would use the `'-'` prefix.
*
* Colors: <ul>
* <li ng-repeat="color in colors | orderBy:'-'">{{color}}</li>
* </ul>
*
* For this simple example, you could have also provided `true` as the addition
* optional parameter which requests a reverse order sort to get the same
* result.
*
* <!-- Same result (descending order) as previous snippet. -->
* Colors: <ul>
* <li ng-repeat="color in colors | orderBy:'':true">{{color}}</li>
* </ul>
*
* Example 2: Complex objects, single expression.
*
* You may provide a more complex expression to sort non-primitives values or
* if you want to sort on a decorated/transformed value.
*
* e.g. Support you have a list `users` that looks like this:
*
* authors = [
* {firstName: 'Emily', lastName: 'Bronte'},
* {firstName: 'Mark', lastName: 'Twain'},
* {firstName: 'Jeffrey', lastName: 'Archer'},
* {firstName: 'Isaac', lastName: 'Asimov'},
* {firstName: 'Oscar', lastName: 'Wilde'},
* ];
*
* If you want to list the authors sorted by `lastName`, you would use
*
* <li ng-repeat="author in authors | orderBy:'lastName'">
* {{author.lastName}}, {{author.firstName
* </li>
*
* The string expression, `'lastName'`, indicates that the sort should be on the
* `lastName` property of each item.
*
* Using the lesson from the previous example, you may sort in reverse order of
* lastName using either of the two methods.
*
* <!-- reverse order of last names -->
* <li ng-repeat="author in authors | orderBy:'-lastName'">
* <!-- also does the same thing -->
* <li ng-repeat="author in authors | orderBy:'lastName':true">
*
* Note that, while we only discussed string expressions, such as `"lastName"`
* or the empty string, you can also directly provide a custom callable that
* will be called to transform the element before a sort.
*
* <li ng-repeat="author in authors | orderBy:getAuthorId">
*
* In the previous snippet, `getAuthorId` would evaluate to a callable when
* evaluated on the [Scope] of the `<li>` element. That callable is called once
* for each element in the list (i.e. each author object) and the sort order is
* determined by the sort order of the value mapped by the callable.
*
* Example 3: List expressions
*
* Both a string expression and the callable expression are simple versions of
* the more general list expression. You may pass a list as the orderBy
* expression and this list may consist of either of the string or callable
* expressions you saw in the previous examples. A list expression indicates
* a list of fallback expressions to use when a comparision results in the items
* being equal.
*
* For example, one might want to sort the authors list, first by last name and
* then by first name when the last names are equal. You would do that like
* this:
*
* <li ng-repeat="author in authors | orderBy:['lastName', 'firstName']">
*
* The items in such a list may either be string expressions or callables. The
* list itself might be provided as an expression that is looked up on the scope
* chain.
*/
@NgFilter(name: 'orderBy')
class OrderByFilter {
Parser _parser;
OrderByFilter(Parser this._parser);
static _nop(e) => e;
static bool _isNonZero(int n) => (n != 0);
static int _returnZero() => 0;
static int _defaultComparator(a, b) => Comparable.compare(a, b);
static int _reverseComparator(a, b) => _defaultComparator(b, a);
static int _compareLists(List a, List b, List<Comparator> comparators) {
return new Iterable.generate(a.length, (i) => comparators[i](a[i], b[i]))
.firstWhere(_isNonZero, orElse: _returnZero);
}
static List _sorted(
List items, List<Mapper> mappers, List<Comparator> comparators, bool descending) {
// Do the standard decorate-sort-undecorate aka Schwartzian dance since Dart
// doesn't support a key/transform parameter to sort().
// Ref: http://en.wikipedia.org/wiki/Schwartzian_transform
mapper(e) => mappers.map((m) => m(e)).toList(growable: false);
List decorated = items.map(mapper).toList(growable: false);
List<int> indices = new Iterable.generate(decorated.length, _nop).toList(growable: false);
comparator(i, j) => _compareLists(decorated[i], decorated[j], comparators);
indices.sort((descending) ? (i, j) => comparator(j, i) : comparator);
return indices.map((i) => items[i]).toList(growable: false);
}
/**
* expression: String/Function or Array of String/Function.
*/
List call(List items, var expression, [bool descending=false]) {
List expressions = null;
if (expression is String || expression is Mapper) {
expressions = [expression];
} else if (expression is List) {
expressions = expression as List;
}
if (expressions == null || expressions.length == 0) {
// AngularJS behavior. You must have an expression to get any work done.
return items;
}
int numExpressions = expressions.length;
List<Mapper> mappers = new List(numExpressions);
List<Comparator> comparators = new List<Comparator>(numExpressions);
for (int i = 0; i < numExpressions; i++) {
expression = expressions[i];
if (expression is String) {
var strExp = expression as String;
var desc = false;
if (strExp.startsWith('-') || strExp.startsWith('+')) {
desc = strExp.startsWith('-');
strExp = strExp.substring(1);
}
comparators[i] = desc ? _reverseComparator : _defaultComparator;
if (strExp == '') {
mappers[i] = _nop;
} else {
var parsed = _parser(strExp);
mappers[i] = (e) => parsed.eval(e, null);
}
} else if (expression is Mapper) {
mappers[i] = (expression as Mapper);
comparators[i] = _defaultComparator;
}
}
return _sorted(items, mappers, comparators, descending);
}
}
part of angular.core.dom;
/**
* Callback function used to notify of attribute changes.
*/
typedef AttributeChanged(String newValue);
/**
* Callback function used to notify of text changes.
*/
abstract class TextChangeListener{
call(String text);
}
/**
* NodeAttrs is a facade for element attributes. The facade is responsible
* for normalizing attribute names as well as allowing access to the
* value of the directive.
*/
class NodeAttrs {
final dom.Element element;
Map<String, List<AttributeChanged>> _observers;
NodeAttrs(dom.Element this.element);
operator [](String name) => element.attributes[_snakeCase(name, '-')];
operator []=(String name, String value) {
name = _snakeCase(name, '-');
if (value == null) {
element.attributes.remove(name);
} else {
element.attributes[name] = value;
}
if (_observers != null && _observers.containsKey(name)) {
_observers[name].forEach((fn) => fn(value));
}
}
/**
* Observe changes to the attribute by invoking the [AttributeChanged]
* function. On registration the [AttributeChanged] function gets invoked
* synchronise with the current value.
*/
observe(String attributeName, AttributeChanged notifyFn) {
attributeName = _snakeCase(attributeName, '-');
if (_observers == null) {
_observers = new Map<String, List<AttributeChanged>>();
}
if (!_observers.containsKey(attributeName)) {
_observers[attributeName] = new List<AttributeChanged>();
}
_observers[attributeName].add(notifyFn);
notifyFn(this[attributeName]);
}
}
/**
* TemplateLoader is an asynchronous access to ShadowRoot which is
* loaded asynchronously. It allows a Component to be notified when its
* ShadowRoot is ready.
*/
class TemplateLoader {
final async.Future<dom.ShadowRoot> _template;
async.Future<dom.ShadowRoot> get template => _template;
TemplateLoader(this._template);
}
var _SNAKE_CASE_REGEXP = new RegExp("[A-Z]");
String _snakeCase(String name, [separator = '_']) {
_snakeReplace(Match match) {
return (match.start != 0 ? separator : '') + match.group(0).toLowerCase();
}
var x= name.replaceAllMapped(_SNAKE_CASE_REGEXP, _snakeReplace);
if (x is String) return x;
throw [];
return x;
}
part of angular.directive;
/**
* Causes the compiler to ignore all Angular directives and markup on descendant
* nodes of the matching element. Note, however, that other directives and
* markup on the element are still processed and that only descending the DOM
* for compilation is prevented.
*
* Example:
*
* <div foo="{{a}}" ng-non-bindable>
* <span ng-bind="b"></span>{{b}}
* </div>
*
* In the above example, because the `div` element has the `ng-non-bindable`
* attribute set on it, the `ng-bind` directive and the interpolation for
* `{{b}}` are not processed because Angular will not process the `span` child
* element. However, the `foo` attribute *will* be interpolated because it is
* not on a child node.
*/
@NgDirective(selector: '[ng-non-bindable]',
children: NgAnnotation.IGNORE_CHILDREN)
class NgNonBindableDirective {}
part of angular.mock;
/**
* A convenient way to assert the order in which the DOM elements are processed.
*
* In your test create:
*
* <div log="foo">...</div>
*
* And then assert:
*
* expect(logger).toEqual(['foo']);
*/
@NgDirective(
selector: '[log]',
map: const {
'log': '@.logMessage'
}
)
class LogAttrDirective implements NgAttachAware {
final Logger log;
String logMessage;
LogAttrDirective(Logger this.log);
attach() => log(logMessage == '' ? 'LOG' : logMessage);
}
/**
* A convenient way to verify that a set of operations executed in a specific
* order. Simply inject the Logger into each operation and call:
*
* operation1(Logger logger) => logger('foo');
* operation2(Logger logger) => logger('bar');
*
* Then in the test:
*
* expect(logger).toEqual(['foo', 'bar']);
*/
class Logger implements List {
final List tokens = [];
/**
* Add string token to the list.
*/
call(dynamic text) => tokens.add(text);
/**
* Return a `;` separated list of recorded tokens.
*/
String result() => tokens.join('; ');
noSuchMethod(Invocation invocation) => mirror.reflect(tokens).delegate(invocation);
}
library angular.tools.common;
class DirectiveInfo {
String selector;
List<String> expressionAttrs = <String>[];
List<String> expressions = <String>[];
DirectiveInfo([this.selector, this.expressionAttrs, this.expressions]) {
if (expressionAttrs == null) {
expressionAttrs = <String>[];
}
if (expressions == null) {
expressions = <String>[];
}
}
}
const String DIRECTIVE = 'DIRECTIVE';
const String COMPONENT = 'COMPONENT';
class DirectiveMetadata {
String className;
String type; // DIRECTIVE/COMPONENT
String selector;
Map<String, String> attributeMappings;
List<String> exportExpressionAttrs;
List<String> exportExpressions;
DirectiveMetadata([this.className, this.type, this.selector,
this.attributeMappings, this.exportExpressionAttrs,
this.exportExpressions]) {
if (attributeMappings == null) {
attributeMappings = <String, String>{};
}
if (exportExpressions == null) {
exportExpressions = <String>[];
}
if (exportExpressionAttrs == null) {
exportExpressionAttrs = <String>[];
}
}
}
part of angular.mock;
List<Function> _asyncQueue = [];
List<_TimerSpec> _timerQueue = [];
List _asyncErrors = [];
bool _noMoreAsync = false;
/**
* Runs any queued up async calls and any async calls queued with
* running microLeap. Example:
*
* it('should run async code', async(() {
* var thenRan = false;
* new Future.value('s').then((_) { thenRan = true; });
* expect(thenRan).toBe(false);
* microLeap();
* expect(thenRan).toBe(true);
* }));
*
* it('should run chained thens', async(() {
* var log = [];
* new Future.value('s')
* .then((_) { log.add('firstThen'); })
* .then((_) { log.add('2ndThen'); });
* expect(log.join(' ')).toEqual('');
* microLeap();
* expect(log.join(' ')).toEqual('firstThen 2ndThen');
* }));
*
*/
microLeap() {
while (!_asyncQueue.isEmpty) {
// copy the queue as it may change.
var toRun = new List.from(_asyncQueue);
_asyncQueue = [];
// TODO: Support the case where multiple exceptions are thrown.
// e.g. with a throwNextException() method.
assert(_asyncErrors.isEmpty);
toRun.forEach((fn) => fn());
if (!_asyncErrors.isEmpty) {
var e = _asyncErrors.removeAt(0);
throw ['Async error', e, dartAsync.getAttachedStackTrace(e)];
}
}
}
/**
* Simulates a clock tick by running any scheduled timers. Can only be used
* in [async] tests.Clock tick will call [microLeap] to process the microtask
* queue before each timer callback.
*
* Note: microtasks scheduled form the last timer are not going to be processed.
*
* Example:
*
* it('should run queued timer after sufficient clock ticks', async(() {
* bool timerRan = false;
* new Timer(new Duration(milliseconds: 10), () => timerRan = true);
*
* clockTick(milliseconds: 9);
* expect(timerRan).toBeFalsy();
* clockTick(milliseconds: 1);
* expect(timerRan).toBeTruthy();
* }));
*
* it('should run periodic timer', async(() {
* int timerRan = 0;
* new Timer.periodic(new Duration(milliseconds: 10), (_) => timerRan++);
*
* clockTick(milliseconds: 9);
* expect(timerRan).toBe(0);
* clockTick(milliseconds: 1);
* expect(timerRan).toBe(1);
* clockTick(milliseconds: 30);
* expect(timerRan).toBe(4);
* }));
*/
clockTick({int days: 0,
int hours: 0,
int minutes: 0,
int seconds: 0,
int milliseconds: 0,
int microseconds: 0}) {
var tickDuration = new Duration(days: days, hours: hours, minutes: minutes,
seconds: seconds, milliseconds: milliseconds, microseconds: microseconds);
var queue = _timerQueue;
var remainingTimers = [];
_timerQueue = [];
queue.forEach((_TimerSpec spec) {
if (!spec.isActive) return; // Skip over inactive timers.
if (spec.periodic) {
// We always add back the periodic timer unless it's cancelled.
remainingTimers.add(spec);
// Ignore ZERO duration ticks for periodic timers.
if (tickDuration == Duration.ZERO) return;
spec.elapsed += tickDuration;
// Run the timer as many times as the timer priod fits into the tick.
while (spec.elapsed >= spec.duration) {
spec.elapsed -= spec.duration;
microLeap();
spec.fn(spec);
}
} else {
spec.duration -= tickDuration;
if (spec.duration <= Duration.ZERO) {
microLeap();
spec.fn();
} else {
remainingTimers.add(spec);
}
}
});
// Remaining timers should come before anything else scheduled after them.
_timerQueue.insertAll(0, remainingTimers);
}
/**
* Causes runAsync calls to throw exceptions.
*
* This function is useful while debugging async tests: the exception
* is thrown from the runAsync call-site instead later in the test.
*/
noMoreAsync() {
_noMoreAsync = true;
}
/**
* Captures all runAsync calls inside of a function.
*
* Typically used within a test: it('should be async', async(() { ... }));
*/
async(Function fn) =>
() {
_noMoreAsync = false;
_asyncErrors = [];
_timerQueue = [];
var zoneSpec = new dartAsync.ZoneSpecification(
scheduleMicrotask: (_, __, ___, asyncFn) {
if (_noMoreAsync) {
throw ['runAsync called after noMoreAsync()'];
} else {
_asyncQueue.add(asyncFn);
}
},
createTimer: (_, __, ____, Duration duration, void f()) =>
_createTimer(f, duration, false),
createPeriodicTimer:
(_, __, ___, Duration period, void f(dartAsync.Timer timer)) =>
_createTimer(f, period, true),
handleUncaughtError: (_, __, ___, e) => _asyncErrors.add(e)
);
dartAsync.runZoned(() {
fn();
microLeap();
}, zoneSpecification: zoneSpec);
_asyncErrors.forEach((e) {
throw "During runZoned: $e. Stack:\n${dartAsync.getAttachedStackTrace(e)}";
});
if (!_timerQueue.isEmpty && _timerQueue.any((_TimerSpec spec) => spec.isActive)) {
throw ["${_timerQueue.where((_TimerSpec spec) => spec.isActive).length} "
"active timer(s) are still in the queue."];
}
};
_createTimer(Function fn, Duration duration, bool periodic) {
var timer = new _TimerSpec(fn, duration, periodic);
_timerQueue.add(timer);
return timer;
}
/**
* Enforces synchronous code. Any calls to runAsync inside of 'sync'
* will throw an exception.
*/
sync(Function fn) => () {
dartAsync.runZoned(fn, zoneSpecification: new dartAsync.ZoneSpecification(
scheduleMicrotask: (_, __, ___, asyncFn) =>
throw ['runAsync called from sync function.'],
createTimer: (_, __, ____, Duration duration, void f()) =>
throw ['Timer created from sync function.'],
createPeriodicTimer:
(_, __, ___, Duration period, void f(dartAsync.Timer timer)) =>
throw ['periodic Timer created from sync function.']
));
};
class _TimerSpec implements dartAsync.Timer {
Function fn;
Duration duration;
Duration elapsed = Duration.ZERO;
bool periodic;
bool isActive = true;
_TimerSpec(this.fn, this.duration, this.periodic);
void cancel() {
isActive = false;
}
}
library angular.core.dom;
import 'dart:async' as async;
import 'dart:json' as json;
import 'dart:html' as dom;
import 'dart:mirrors';
import 'package:di/di.dart';
import 'package:perf_api/perf_api.dart';
import '../core/module.dart';
import '../core/parser/parser_library.dart';
part 'block.dart';
part 'block_factory.dart';
part 'common.dart';
part 'compiler.dart';
part 'directive.dart';
part 'http.dart';
part 'ng_mustache.dart';
part 'node_cursor.dart';
part 'selector.dart';
part 'template_cache.dart';
part 'tree_sanitizer.dart';
class NgCoreDomModule extends Module {
NgCoreDomModule() {
value(TextChangeListener, null);
factory(TemplateCache, (_) => new TemplateCache(capacity: 0));
type(dom.NodeTreeSanitizer, implementedBy: NullTreeSanitizer);
type(NgTextMustacheDirective);
type(NgAttrMustacheDirective);
type(Compiler);
type(Http);
type(UrlRewriter);
factory(HttpBackend, (i) { throw "Why not Override????"; });
type(HttpDefaultHeaders);
type(HttpDefaults);
type(HttpInterceptors);
type(BlockCache);
type(GetterSetter);
}
}
library selector;
import 'package:html5lib/dom.dart';
class ContainsSelector {
String selector;
RegExp regexp;
ContainsSelector(this.selector, regexp) {
this.regexp = new RegExp(regexp);
}
}
RegExp _SELECTOR_REGEXP = new RegExp(r'^(?:([\w\-]+)|(?:\.([\w\-]+))|(?:\[([\w\-]+)(?:=([^\]]*))?\]))');
RegExp _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([\w\-]+)(?:\=(.*))?\]$');
RegExp _CONTAINS_REGEXP = new RegExp(r'^:contains\(\/(.+)\/\)$'); //
RegExp _ATTR_CONTAINS_REGEXP = new RegExp(r'^\[\*=\/(.+)\/\]$'); //
class _SelectorPart {
final String element;
final String className;
final String attrName;
final String attrValue;
const _SelectorPart.fromElement(String this.element)
: className = null, attrName = null, attrValue = null;
const _SelectorPart.fromClass(String this.className)
: element = null, attrName = null, attrValue = null;
const _SelectorPart.fromAttribute(String this.attrName, String this.attrValue)
: element = null, className = null;
toString() =>
element == null
? (className == null
? (attrValue == '' ? '[$attrName]' : '[$attrName=$attrValue]')
: '.$className')
: element;
}
List<_SelectorPart> _splitCss(String selector) {
List<_SelectorPart> parts = [];
var remainder = selector;
var match;
while (!remainder.isEmpty) {
if ((match = _SELECTOR_REGEXP.firstMatch(remainder)) != null) {
if (match[1] != null) {
parts.add(new _SelectorPart.fromElement(match[1].toLowerCase()));
} else if (match[2] != null) {
parts.add(new _SelectorPart.fromClass(match[2].toLowerCase()));
} else if (match[3] != null) {
var attrValue = match[4] == null ? '' : match[4].toLowerCase();
parts.add(new _SelectorPart.fromAttribute(match[3].toLowerCase(),
attrValue));
} else {
throw "Missmatched RegExp $_SELECTOR_REGEXP on $remainder";
}
} else {
throw "Unknown selector format '$remainder'.";
}
remainder = remainder.substring(match.end);
}
return parts;
}
bool matchesNode(Node node, String selector) {
var match, selectorParts;
if ((match = _CONTAINS_REGEXP.firstMatch(selector)) != null) {
if (node is! Text) {
return false;
}
return new RegExp(match.group(1)).hasMatch((node as Text).value);
} else if ((match = _ATTR_CONTAINS_REGEXP.firstMatch(selector)) != null) {
if (node is! Element) {
return false;
}
var regexp = new RegExp(match.group(1));
for (String attrName in node.attributes.keys) {
if (regexp.hasMatch(node.attributes[attrName])) {
return true;
}
}
return false;
} else if ((selectorParts = _splitCss(selector)) != null) {
if (node is! Element) {
return false;
}
String nodeName = node.tagName.toLowerCase();
bool stillGood = true;
selectorParts.forEach((_SelectorPart part) {
if (part.element != null) {
if (nodeName != part.element) {
stillGood = false;
}
} else if (part.className != null) {
if (node.attributes['class'] == null ||
!node.attributes['class'].split(' ').contains(part.className)) {
stillGood = false;
}
} else if (part.attrName != null) {
if (part.attrValue == '' ?
node.attributes[part.attrName] == null :
node.attributes[part.attrName] != part.attrValue) {
stillGood = false;
}
}
});
return stillGood;
} else {
throw new ArgumentError('Unsupported Selector: $selector');
}
switch(node.nodeType) {
case 1: // Element
break;
case 3: // Text Node
break;
}
return false;
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
/**
* This is a private class internal to DateFormat which is used for formatting
* particular fields in a template. e.g. if the format is hh:mm:ss then the
* fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows
* how to format that portion of a date.
*/
abstract class _DateFormatField {
/** The format string that defines us, e.g. "hh" */
String pattern;
/** The DateFormat that we are part of.*/
DateFormat parent;
_DateFormatField(this.pattern, this.parent);
/**
* Return the width of [pattern]. Different widths represent different
* formatting options. See the comment for DateFormat for details.
*/
int get width => pattern.length;
String fullPattern() => pattern;
String toString() => pattern;
/** Format date according to our specification and return the result. */
String format(DateTime date) {
// Default implementation in the superclass, works for both types of
// literal patterns, and is overridden by _DateFormatPatternField.
return pattern;
}
/** Abstract method for subclasses to implementing parsing for their format.*/
void parse(_Stream input, _DateBuilder dateFields);
/** Parse a literal field. We just look for the exact input. */
void parseLiteral(_Stream input) {
var found = input.read(width);
if (found != pattern) {
throwFormatException(input);
}
}
/** Throw a format exception with an error message indicating the position.*/
void throwFormatException(_Stream stream) {
throw new FormatException("Trying to read $this from ${stream.contents} "
"at position ${stream.index}");
}
}
/**
* Represents a literal field - a sequence of characters that doesn't
* change according to the date's data. As such, the implementation
* is extremely simple.
*/
class _DateFormatLiteralField extends _DateFormatField {
_DateFormatLiteralField(pattern, parent): super(pattern, parent);
parse(_Stream input, _DateBuilder dateFields) {
return parseLiteral(input);
}
}
/**
* Represents a literal field with quoted characters in it. This is
* only slightly more complex than a _DateFormatLiteralField.
*/
class _DateFormatQuotedField extends _DateFormatField {
String _fullPattern;
String fullPattern() => _fullPattern;
_DateFormatQuotedField(pattern, parent): super(pattern, parent) {
_fullPattern = pattern;
patchQuotes();
}
parse(_Stream input, _DateBuilder dateFields) {
return parseLiteral(input);
}
void patchQuotes() {
if (pattern == "''") {
pattern = "'";
} else {
pattern = pattern.substring(1, pattern.length - 1);
var twoEscapedQuotes = new RegExp(r"''");
pattern = pattern.replaceAll(twoEscapedQuotes, "'");
}
}
}
/*
* Represents a field in the pattern that formats some aspect of the
* date. Consists primarily of a switch on the particular pattern characters
* to determine what to do.
*/
class _DateFormatPatternField extends _DateFormatField {
_DateFormatPatternField(pattern, parent): super(pattern, parent);
/** Format date according to our specification and return the result. */
String format(DateTime date) {
return formatField(date);
}
/**
* Parse the date according to our specification and put the result
* into the correct place in dateFields.
*/
void parse(_Stream input, _DateBuilder dateFields) {
parseField(input, dateFields);
}
/**
* Parse a field representing part of a date pattern. Note that we do not
* return a value, but rather build up the result in [builder].
*/
void parseField(_Stream input, _DateBuilder builder) {
try {
switch(pattern[0]) {
case 'a': parseAmPm(input, builder); break;
case 'c': parseStandaloneDay(input); break;
case 'd': handleNumericField(input, builder.setDay); break; // day
// Day of year. Setting month=January with any day of the year works
case 'D': handleNumericField(input, builder.setDay); break; // dayofyear
case 'E': parseDayOfWeek(input); break;
case 'G': break; // era
case 'h': parse1To12Hours(input, builder); break;
case 'H': handleNumericField(input, builder.setHour); break; // hour 0-23
case 'K': handleNumericField(input, builder.setHour); break; //hour 0-11
case 'k': handleNumericField(input, builder.setHour,-1); break; //hr 1-24
case 'L': parseStandaloneMonth(input, builder); break;
case 'M': parseMonth(input, builder); break;
case 'm': handleNumericField(input, builder.setMinute); break; // minutes
case 'Q': break; // quarter
case 'S': handleNumericField(input, builder.setFractionalSecond); break;
case 's': handleNumericField(input, builder.setSecond); break;
case 'v': break; // time zone id
case 'y': handleNumericField(input, builder.setYear); break;
case 'z': break; // time zone
case 'Z': break; // time zone RFC
default: return;
}
} catch (e) { throwFormatException(input); }
}
/** Formatting logic if we are of type FIELD */
String formatField(DateTime date) {
switch (pattern[0]) {
case 'a': return formatAmPm(date);
case 'c': return formatStandaloneDay(date);
case 'd': return formatDayOfMonth(date);
case 'D': return formatDayOfYear(date);
case 'E': return formatDayOfWeek(date);
case 'G': return formatEra(date);
case 'h': return format1To12Hours(date);
case 'H': return format0To23Hours(date);
case 'K': return format0To11Hours(date);
case 'k': return format24Hours(date);
case 'L': return formatStandaloneMonth(date);
case 'M': return formatMonth(date);
case 'm': return formatMinutes(date);
case 'Q': return formatQuarter(date);
case 'S': return formatFractionalSeconds(date);
case 's': return formatSeconds(date);
case 'v': return formatTimeZoneId(date);
case 'y': return formatYear(date);
case 'z': return formatTimeZone(date);
case 'Z': return formatTimeZoneRFC(date);
default: return '';
}
}
/** Return the symbols for our current locale. */
DateSymbols get symbols => dateTimeSymbols[parent.locale];
formatEra(DateTime date) {
var era = date.year > 0 ? 1 : 0;
return width >= 4 ? symbols.ERANAMES[era] :
symbols.ERAS[era];
}
formatYear(DateTime date) {
// TODO(alanknight): Proper handling of years <= 0
var year = date.year;
if (year < 0) {
year = -year;
}
return width == 2 ? padTo(2, year % 100) : year.toString();
}
/**
* We are given [input] as a stream from which we want to read a date. We
* can't dynamically build up a date, so we are given a list [dateFields] of
* the constructor arguments and an [position] at which to set it
* (year,month,day,hour,minute,second,fractionalSecond)
* then after all parsing is done we construct a date from the arguments.
* This method handles reading any of the numeric fields. The [offset]
* argument allows us to compensate for zero-based versus one-based values.
*/
void handleNumericField(_Stream input, Function setter, [int offset = 0]) {
var result = input.nextInteger();
if (result == null) throwFormatException(input);
setter(result + offset);
}
/**
* We are given [input] as a stream from which we want to read a date. We
* can't dynamically build up a date, so we are given a list [dateFields] of
* the constructor arguments and an [position] at which to set it
* (year,month,day,hour,minute,second,fractionalSecond)
* then after all parsing is done we construct a date from the arguments.
* This method handles reading any of string fields from an enumerated set.
*/
int parseEnumeratedString(_Stream input, List possibilities) {
var results = new _Stream(possibilities).findIndexes(
(each) => input.peek(each.length) == each);
if (results.isEmpty) throwFormatException(input);
results.sort(
(a, b) => possibilities[a].length.compareTo(possibilities[b].length));
var longestResult = results.last;
input.read(possibilities[longestResult].length);
return longestResult;
}
String formatMonth(DateTime date) {
switch (width) {
case 5: return symbols.NARROWMONTHS[date.month-1];
case 4: return symbols.MONTHS[date.month-1];
case 3: return symbols.SHORTMONTHS[date.month-1];
default:
return padTo(width, date.month);
}
}
void parseMonth(input, dateFields) {
var possibilities;
switch(width) {
case 5: possibilities = symbols.NARROWMONTHS; break;
case 4: possibilities = symbols.MONTHS; break;
case 3: possibilities = symbols.SHORTMONTHS; break;
default: return handleNumericField(input, dateFields.setMonth);
}
dateFields.month = parseEnumeratedString(input, possibilities) + 1;
}
String format24Hours(DateTime date) {
return padTo(width, date.hour);
}
String formatFractionalSeconds(DateTime date) {
// Always print at least 3 digits. If the width is greater, append 0s
var basic = padTo(3, date.millisecond);
if (width - 3 > 0) {
var extra = padTo(width - 3, 0);
return basic + extra;
} else {
return basic;
}
}
String formatAmPm(DateTime date) {
var hours = date.hour;
var index = (date.hour >= 12) && (date.hour < 24) ? 1 : 0;
var ampm = symbols.AMPMS;
return ampm[index];
}
void parseAmPm(input, dateFields) {
// If we see a "PM" note it in an extra field.
var ampm = parseEnumeratedString(input, symbols.AMPMS);
if (ampm == 1) dateFields.pm = true;
}
String format1To12Hours(DateTime date) {
var hours = date.hour;
if (date.hour > 12) hours = hours - 12;
if (hours == 0) hours = 12;
return padTo(width, hours);
}
void parse1To12Hours(_Stream input, _DateBuilder dateFields) {
handleNumericField(input, dateFields.setHour);
if (dateFields.hour == 12) dateFields.hour = 0;
}
String format0To11Hours(DateTime date) {
return padTo(width, date.hour % 12);
}
String format0To23Hours(DateTime date) {
return padTo(width, date.hour);
}
String formatStandaloneDay(DateTime date) {
switch (width) {
case 5: return symbols.STANDALONENARROWWEEKDAYS[date.weekday % 7];
case 4: return symbols.STANDALONEWEEKDAYS[date.weekday % 7];
case 3: return symbols.STANDALONESHORTWEEKDAYS[date.weekday % 7];
default:
return padTo(1, date.day);
}
}
void parseStandaloneDay(_Stream input) {
// This is ignored, but we still have to skip over it the correct amount.
var possibilities;
switch(width) {
case 5: possibilities = symbols.STANDALONENARROWWEEKDAYS; break;
case 4: possibilities = symbols.STANDALONEWEEKDAYS; break;
case 3: possibilities = symbols.STANDALONESHORTWEEKDAYS; break;
default: return handleNumericField(input, (x)=>x);
}
parseEnumeratedString(input, possibilities);
}
String formatStandaloneMonth(DateTime date) {
switch (width) {
case 5:
return symbols.STANDALONENARROWMONTHS[date.month-1];
case 4:
return symbols.STANDALONEMONTHS[date.month-1];
case 3:
return symbols.STANDALONESHORTMONTHS[date.month-1];
default:
return padTo(width, date.month);
}
}
void parseStandaloneMonth(input, dateFields) {
var possibilities;
switch(width) {
case 5: possibilities = symbols.STANDALONENARROWMONTHS; break;
case 4: possibilities = symbols.STANDALONEMONTHS; break;
case 3: possibilities = symbols.STANDALONESHORTMONTHS; break;
default: return handleNumericField(input, dateFields.setMonth);
}
dateFields.month = parseEnumeratedString(input, possibilities) + 1;
}
String formatQuarter(DateTime date) {
var quarter = ((date.month - 1) / 3).truncate();
if (width < 4) {
return symbols.SHORTQUARTERS[quarter];
} else {
return symbols.QUARTERS[quarter];
}
}
String formatDayOfMonth(DateTime date) {
return padTo(width, date.day);
}
String formatDayOfYear(DateTime date) => padTo(width, dayNumberInYear(date));
/** Return the ordinal day, i.e. the day number in the year. */
int dayNumberInYear(DateTime date) {
if (date.month == 1) return date.day;
if (date.month == 2) return date.day + 31;
return ordinalDayFromMarchFirst(date) + 59 + (isLeapYear(date) ? 1 : 0);
}
/**
* Return the day of the year counting March 1st as 1, after which the
* number of days per month is constant, so it's easier to calculate.
* Formula from http://en.wikipedia.org/wiki/Ordinal_date
*/
int ordinalDayFromMarchFirst(DateTime date) =>
((30.6 * date.month) - 91.4).floor() + date.day;
/**
* Return true if this is a leap year. Rely on [DateTime] to do the
* underlying calculation, even though it doesn't expose the test to us.
*/
bool isLeapYear(DateTime date) {
var feb29 = new DateTime(date.year, 2, 29);
return feb29.month == 2;
}
String formatDayOfWeek(DateTime date) {
// Note that Dart's weekday returns 1 for Monday and 7 for Sunday.
return (width >= 4 ? symbols.WEEKDAYS :
symbols.SHORTWEEKDAYS)[(date.weekday) % 7];
}
void parseDayOfWeek(_Stream input) {
// This is IGNORED, but we still have to skip over it the correct amount.
var possibilities = width >= 4 ? symbols.WEEKDAYS : symbols.SHORTWEEKDAYS;
parseEnumeratedString(input, possibilities);
}
String formatMinutes(DateTime date) {
return padTo(width, date.minute);
}
String formatSeconds(DateTime date) {
return padTo(width, date.second);
}
String formatTimeZoneId(DateTime date) {
// TODO(alanknight): implement time zone support
throw new UnimplementedError();
}
String formatTimeZone(DateTime date) {
throw new UnimplementedError();
}
String formatTimeZoneRFC(DateTime date) {
throw new UnimplementedError();
}
/**
* Return a string representation of the object padded to the left with
* zeros. Primarily useful for numbers.
*/
String padTo(int width, Object toBePrinted) {
var basicString = toBePrinted.toString();
if (basicString.length >= width) return basicString;
var buffer = new StringBuffer();
for (var i = 0; i < width - basicString.length; i++) {
buffer.write('0');
}
buffer.write(basicString);
return buffer.toString();
}
}
library source_crawler;
import 'package:analyzer_experimental/src/generated/ast.dart';
typedef CompilationUnitVisitor(CompilationUnit cu);
/**
* Dart source file crawler. As it crawls Dart source, it calls
* [CompilationUnitVisitor] on each file.
*/
abstract class SourceCrawler {
void crawl(String entryPoint, CompilationUnitVisitor visitor);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
/**
* A class for holding onto the data for a date so that it can be built
* up incrementally.
*/
class _DateBuilder {
// Default the date values to the EPOCH so that there's a valid date
// in case the format doesn't set them.
int year = 1970,
month = 1,
day = 1,
hour = 0,
minute = 0,
second = 0,
fractionalSecond = 0;
bool pm = false;
bool utc = false;
// Functions that exist just to be closurized so we can pass them to a general
// method.
void setYear(x) { year = x; }
void setMonth(x) { month = x; }
void setDay(x) { day = x; }
void setHour(x) { hour = x; }
void setMinute(x) { minute = x; }
void setSecond(x) { second = x; }
void setFractionalSecond(x) { fractionalSecond = x; }
/**
* Return a date built using our values. If no date portion is set,
* use the "Epoch" of January 1, 1970.
*/
DateTime asDate() {
// TODO(alanknight): Validate the date, especially for things which
// can crash the VM, e.g. large month values.
if (utc) {
return new DateTime.utc(
year,
month,
day,
pm ? hour + 12 : hour,
minute,
second,
fractionalSecond);
} else {
return new DateTime(
year,
month,
day,
pm ? hour + 12 : hour,
minute,
second,
fractionalSecond);
}
}
}
/**
* A simple and not particularly general stream class to make parsing
* dates from strings simpler. It is general enough to operate on either
* lists or strings.
*/
class _Stream {
var contents;
int index = 0;
_Stream(this.contents);
bool atEnd() => index >= contents.length;
next() => contents[index++];
/**
* Return the next [howMany] items, or as many as there are remaining.
* Advance the stream by that many positions.
*/
read([howMany = 1]) {
var result = peek(howMany);
index += howMany;
return result;
}
/**
* Return the next [howMany] items, or as many as there are remaining.
* Does not modify the stream position.
*/
peek([howMany = 1]) {
var result;
if (contents is String) {
result = contents.substring(
index,
min(index + howMany, contents.length));
} else {
// Assume List
result = contents.sublist(index, index + howMany);
}
return result;
}
/** Return the remaining contents of the stream */
rest() => peek(contents.length - index);
/**
* Find the index of the first element for which [f] returns true.
* Advances the stream to that position.
*/
int findIndex(Function f) {
while (!atEnd()) {
if (f(next())) return index - 1;
}
return null;
}
/**
* Find the indexes of all the elements for which [f] returns true.
* Leaves the stream positioned at the end.
*/
List findIndexes(Function f) {
var results = [];
while (!atEnd()) {
if (f(next())) results.add(index - 1);
}
return results;
}
/**
* Assuming that the contents are characters, read as many digits as we
* can see and then return the corresponding integer. Advance the stream.
*/
var digitMatcher = new RegExp(r'\d+');
int nextInteger() {
var string = digitMatcher.stringMatch(rest());
if (string == null || string.isEmpty) return null;
read(string.length);
return int.parse(string);
}
}
library angular.source_crawler_impl;
import 'dart:io';
import 'package:analyzer_experimental/analyzer.dart';
import 'source_crawler.dart';
const String PACKAGE_PREFIX = 'package:';
/**
* Dart source file crawler. As it crawls Dart source, it calls
* [CompilationUnitVisitor] on each file.
*/
class SourceCrawlerImpl implements SourceCrawler {
final List<String> packageRoots;
SourceCrawlerImpl(this.packageRoots);
void crawl(String entryPoint, CompilationUnitVisitor visitor) {
List<String> visited = <String>[];
List<String> toVisit = <String>[];
if (entryPoint.startsWith(PACKAGE_PREFIX)) {
var path = resolvePackagePath(entryPoint);
if (path == null) {
throw 'Unable to resolve $entryPoint';
}
toVisit.add(path);
} else {
toVisit.add(entryPoint);
}
while (toVisit.isNotEmpty) {
var currentFile = toVisit.removeAt(0);
visited.add(currentFile);
var currentDir = new File(currentFile).directory.path;
CompilationUnit cu = parseDartFile(currentFile);
processImports(cu, currentDir, currentFile, visited, toVisit);
visitor(cu);
}
}
void processImports(CompilationUnit cu, String currentDir,
String currentFile, List<String> visited,
List<String> toVisit) {
cu.directives.forEach((Directive directive) {
if (directive is ImportDirective || directive is PartDirective) {
UriBasedDirective import = directive;
String canonicalFile = canonicalizeImportPath(
currentDir, currentFile, import.uri.stringValue);
if (canonicalFile == null) return;
if (!visited.contains(canonicalFile) &&
!toVisit.contains(canonicalFile)) {
toVisit.add(canonicalFile);
}
}
});
}
String canonicalizeImportPath(String currentDir,
String currentFile,
String uri) {
// ignore core libraries
if (uri.startsWith('dart:')) {
return null;
}
if (uri.startsWith(PACKAGE_PREFIX)) {
return resolvePackagePath(uri);
}
// relative import.
if (uri.startsWith('../')) {
while (uri.startsWith('../')) {
uri = uri.substring('../'.length);
currentDir = currentDir.substring(0, currentDir.lastIndexOf('/'));
}
}
return '$currentDir/$uri';
}
String resolvePackagePath(String uri) {
for (String packageRoot in packageRoots) {
var resolvedPath = _packageUriResolver(uri, packageRoot);
if (new File(resolvedPath).existsSync()) {
return resolvedPath;
}
}
return null;
}
String _packageUriResolver(String uri, String packageRoot) {
var packagePath = uri.substring(PACKAGE_PREFIX.length);
if (!packageRoot.endsWith('/')) {
packageRoot = packageRoot + '/';
}
return packageRoot + packagePath;
}
}
part of angular.mock;
/*
* Use Probe directive to capture the Scope, Injector and Element from any DOM
* location into root-scope. This is useful for testing to get a hold of
* any directive.
*
* <div some-directive probe="myProbe">..</div>
*
* rootScope.myProbe.directive(SomeAttrDirective);
*/
@NgDirective(selector: '[probe]')
class Probe implements NgDetachAware {
final Scope scope;
final Injector injector;
final Element element;
final NodeAttrs _attrs;
Probe(Scope this.scope, Injector this.injector, Element this.element, NodeAttrs this._attrs) {
scope.$root[_attrs['probe']] = this;
}
detach() => scope.$root[_attrs['probe']] = null;
/**
* Retrieve a Directive at the current element.
*/
directive(Type type) => injector.get(type);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
/**
* Bidi stands for Bi-directional text.
* According to http://en.wikipedia.org/wiki/Bi-directional_text:
* Bi-directional text is text containing text in both text directionalities,
* both right-to-left (RTL) and left-to-right (LTR). It generally involves text
* containing different types of alphabets, but may also refer to boustrophedon,
* which is changing text directionality in each row.
*
* This file provides some utility classes for determining directionality of
* text, switching CSS layout from LTR to RTL, and other normalizing utilities
* needed when switching between RTL and LTR formatting.
*
* It defines the TextDirection class which is used to represent directionality
* of text,
* In most cases, it is preferable to use bidi_formatter.dart, which provides
* bidi functionality in the given directional context, instead of using
* bidi_utils.dart directly.
*/
class TextDirection {
static const LTR = const TextDirection._('LTR', 'ltr');
static const RTL = const TextDirection._('RTL', 'rtl');
// If the directionality of the text cannot be determined and we are not using
// the context direction (or if the context direction is unknown), then the
// text falls back on the more common ltr direction.
static const UNKNOWN = const TextDirection._('UNKNOWN', 'ltr');
/**
* Textual representation of the directionality constant. One of
* 'LTR', 'RTL', or 'UNKNOWN'.
*/
final String value;
/** Textual representation of the directionality when used in span tag. */
final String spanText;
const TextDirection._(this.value, this.spanText);
/**
* Returns true if [otherDirection] is known to be different from this
* direction.
*/
bool isDirectionChange(TextDirection otherDirection) {
return otherDirection != TextDirection.UNKNOWN && this != otherDirection;
}
}
/**
* This provides utility methods for working with bidirectional text. All
* of the methods are static, and are organized into a class primarily to
* group them together for documentation and discoverability.
*/
class Bidi {
/** Unicode "Left-To-Right Embedding" (LRE) character. */
static const LRE = '\u202A';
/** Unicode "Right-To-Left Embedding" (RLE) character. */
static const RLE = '\u202B';
/** Unicode "Pop Directional Formatting" (PDF) character. */
static const PDF = '\u202C';
/** Unicode "Left-To-Right Mark" (LRM) character. */
static const LRM = '\u200E';
/** Unicode "Right-To-Left Mark" (RLM) character. */
static const RLM = '\u200F';
/** Constant to define the threshold of RTL directionality. */
static num _RTL_DETECTION_THRESHOLD = 0.40;
/**
* Practical patterns to identify strong LTR and RTL characters, respectively.
* These patterns are not completely correct according to the Unicode
* standard. They are simplified for performance and small code size.
*/
static const String _LTR_CHARS =
r'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590'
r'\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF';
static const String _RTL_CHARS = r'\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
/**
* Returns the input [text] with spaces instead of HTML tags or HTML escapes,
* which is helpful for text directionality estimation.
* Note: This function should not be used in other contexts.
* It does not deal well with many things: comments, script,
* elements, style elements, dir attribute,`>` in quoted attribute values,
* etc. But it does handle well enough the most common use cases.
* Since the worst that can happen as a result of these shortcomings is that
* the wrong directionality will be estimated, we have not invested in
* improving this.
*/
static String stripHtmlIfNeeded(String text) {
// The regular expression is simplified for an HTML tag (opening or
// closing) or an HTML escape. We might want to skip over such expressions
// when estimating the text directionality.
return text.replaceAll(new RegExp(r'<[^>]*>|&[^;]+;'), ' ');
}
/**
* Determines if the first character in [text] with strong directionality is
* LTR. If [isHtml] is true, the text is HTML or HTML-escaped.
*/
static bool startsWithLtr(String text, [isHtml=false]) {
return new RegExp('^[^$_RTL_CHARS]*[$_LTR_CHARS]').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Determines if the first character in [text] with strong directionality is
* RTL. If [isHtml] is true, the text is HTML or HTML-escaped.
*/
static bool startsWithRtl(String text, [isHtml=false]) {
return new RegExp('^[^$_LTR_CHARS]*[$_RTL_CHARS]').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Determines if the exit directionality (ie, the last strongly-directional
* character in [text] is LTR. If [isHtml] is true, the text is HTML or
* HTML-escaped.
*/
static bool endsWithLtr(String text, [isHtml=false]) {
return new RegExp('[$_LTR_CHARS][^$_RTL_CHARS]*\$').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Determines if the exit directionality (ie, the last strongly-directional
* character in [text] is RTL. If [isHtml] is true, the text is HTML or
* HTML-escaped.
*/
static bool endsWithRtl(String text, [isHtml=false]) {
return new RegExp('[$_RTL_CHARS][^$_LTR_CHARS]*\$').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Determines if the given [text] has any LTR characters in it.
* If [isHtml] is true, the text is HTML or HTML-escaped.
*/
static bool hasAnyLtr(String text, [isHtml=false]) {
return new RegExp(r'[' '$_LTR_CHARS' r']').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Determines if the given [text] has any RTL characters in it.
* If [isHtml] is true, the text is HTML or HTML-escaped.
*/
static bool hasAnyRtl(String text, [isHtml=false]) {
return new RegExp(r'[' '$_RTL_CHARS' r']').hasMatch(
isHtml? stripHtmlIfNeeded(text) : text);
}
/**
* Check if a BCP 47 / III [languageString] indicates an RTL language.
*
* i.e. either:
* - a language code explicitly specifying one of the right-to-left scripts,
* e.g. "az-Arab", or
* - a language code specifying one of the languages normally written in a
* right-to-left script, e.g. "fa" (Farsi), except ones explicitly
* specifying Latin or Cyrillic script (which are the usual LTR
* alternatives).
*
* The list of right-to-left scripts appears in the 100-199 range in
* http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
* Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
* Tifinagh, which also have significant modern usage. The rest (Syriac,
* Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
* and are not recognized.
* The languages usually written in a right-to-left script are taken as those
* with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
* http://www.iana.org/assignments/language-subtag-registry,
* as well as Sindhi (sd) and Uyghur (ug).
* The presence of other subtags of the language code, e.g. regions like EG
* (Egypt), is ignored.
*/
static bool isRtlLanguage(String languageString) {
return new RegExp(r'^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_]'
r'(Arab|Hebr|Thaa|Nkoo|Tfng))(?!.*[-_](Latn|Cyrl)($|-|_))'
r'($|-|_)', caseSensitive: false).hasMatch(languageString);
}
/**
* Enforce the [html] snippet in RTL directionality regardless of overall
* context. If the html piece was enclosed by a tag, the direction will be
* applied to existing tag, otherwise a span tag will be added as wrapper.
* For this reason, if html snippet start with with tag, this tag must enclose
* the whole piece. If the tag already has a direction specified, this new one
* will override existing one in behavior (should work on Chrome, FF, and IE
* since this was ported directly from the Closure version).
*/
static String enforceRtlInHtml(String html) {
return _enforceInHtmlHelper(html, 'rtl');
}
/**
* Enforce RTL on both end of the given [text] using unicode BiDi formatting
* characters RLE and PDF.
*/
static String enforceRtlInText(String text) {
return '$RLE$text$PDF';
}
/**
* Enforce the [html] snippet in LTR directionality regardless of overall
* context. If the html piece was enclosed by a tag, the direction will be
* applied to existing tag, otherwise a span tag will be added as wrapper.
* For this reason, if html snippet start with with tag, this tag must enclose
* the whole piece. If the tag already has a direction specified, this new one
* will override existing one in behavior (tested on FF and IE).
*/
static String enforceLtrInHtml(String html) {
return _enforceInHtmlHelper(html, 'ltr');
}
/**
* Enforce LTR on both end of the given [text] using unicode BiDi formatting
* characters LRE and PDF.
*/
static String enforceLtrInText(String text) {
return '$LRE$text$PDF';
}
/**
* Enforce the [html] snippet in the desired [direction] regardless of overall
* context. If the html piece was enclosed by a tag, the direction will be
* applied to existing tag, otherwise a span tag will be added as wrapper.
* For this reason, if html snippet start with with tag, this tag must enclose
* the whole piece. If the tag already has a direction specified, this new one
* will override existing one in behavior (tested on FF and IE).
*/
static String _enforceInHtmlHelper(String html, String direction) {
if (html.startsWith('<')) {
StringBuffer buffer = new StringBuffer();
var startIndex = 0;
Match match = new RegExp('<\\w+').firstMatch(html);
if (match != null) {
buffer..write(html.substring(startIndex, match.end))
..write(' dir=$direction');
startIndex = match.end;
}
return (buffer..write(html.substring(startIndex))).toString();
}
// '\n' is important for FF so that it won't incorrectly merge span groups.
return '\n<span dir=$direction>$html</span>';
}
/**
* Apply bracket guard to [str] using html span tag. This is to address the
* problem of messy bracket display that frequently happens in RTL layout.
* If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
* directionality, regardless of the estimated directionality.
*/
static String guardBracketInHtml(String str, [bool isRtlContext]) {
var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
RegExp matchingBrackets =
new RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?(>)+)');
return _guardBracketHelper(str, matchingBrackets,
'<span dir=${useRtl? "rtl" : "ltr"}>', '</span>');
}
/**
* Apply bracket guard to [str] using LRM and RLM. This is to address the
* problem of messy bracket display that frequently happens in RTL layout.
* This version works for both plain text and html, but in some cases is not
* as good as guardBracketInHtml.
* If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
* directionality, regardless of the estimated directionality.
*/
static String guardBracketInText(String str, [bool isRtlContext]) {
var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
var mark = useRtl ? RLM : LRM;
return _guardBracketHelper(str,
new RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)'), mark, mark);
}
/**
* (Mostly) reimplements the $& functionality of "replace" in JavaScript.
* Given a [str] and the [regexp] to match with, optionally supply a string to
* be inserted [before] the match and/or [after]. For example,
* `_guardBracketHelper('firetruck', new RegExp('truck'), 'hydrant', '!')`
* would return 'firehydrant!'.
*/
// TODO(efortuna): Get rid of this once this is implemented in Dart.
// See Issue 2979.
static String _guardBracketHelper(String str, RegExp regexp, [String before,
String after]) {
StringBuffer buffer = new StringBuffer();
var startIndex = 0;
Iterable matches = regexp.allMatches(str);
for (Match match in matches) {
buffer..write(str.substring(startIndex, match.start))
..write(before)
..write(str.substring(match.start, match.end))
..write(after);
startIndex = match.end;
}
return (buffer..write(str.substring(startIndex))).toString();
}
/**
* Estimates the directionality of [text] using the best known
* general-purpose method (using relative word counts). A
* TextDirection.UNKNOWN return value indicates completely neutral input.
* [isHtml] is true if [text] HTML or HTML-escaped.
*
* If the number of RTL words is above a certain percentage of the total
* number of strongly directional words, returns RTL.
* Otherwise, if any words are strongly or weakly LTR, returns LTR.
* Otherwise, returns UNKNOWN, which is used to mean `neutral`.
* Numbers and URLs are counted as weakly LTR.
*/
static TextDirection estimateDirectionOfText(String text,
{bool isHtml: false}) {
text = isHtml? stripHtmlIfNeeded(text) : text;
var rtlCount = 0;
var total = 0;
var hasWeaklyLtr = false;
// Split a string into 'words' for directionality estimation based on
// relative word counts.
for (String token in text.split(new RegExp(r'\s+'))) {
if (startsWithRtl(token)) {
rtlCount++;
total++;
} else if (new RegExp(r'^http://').hasMatch(token)) {
// Checked if token looks like something that must always be LTR even in
// RTL text, such as a URL.
hasWeaklyLtr = true;
} else if (hasAnyLtr(token)) {
total++;
} else if (new RegExp(r'\d').hasMatch(token)) {
// Checked if token contains any numerals.
hasWeaklyLtr = true;
}
}
if (total == 0) {
return hasWeaklyLtr ? TextDirection.LTR : TextDirection.UNKNOWN;
} else if (rtlCount > _RTL_DETECTION_THRESHOLD * total) {
return TextDirection.RTL;
} else {
return TextDirection.LTR;
}
}
/**
* Replace the double and single quote directly after a Hebrew character in
* [str] with GERESH and GERSHAYIM. This is most likely the user's intention.
*/
static String normalizeHebrewQuote(String str) {
StringBuffer buf = new StringBuffer();
if (str.length > 0) {
buf.write(str.substring(0, 1));
}
// Start at 1 because we're looking for the patterns [\u0591-\u05f2])" or
// [\u0591-\u05f2]'.
for (int i = 1; i < str.length; i++) {
if (str.substring(i, i+1) == '"'
&& new RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) {
buf.write('\u05f4');
} else if (str.substring(i, i+1) == "'"
&& new RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) {
buf.write('\u05f3');
} else {
buf.write(str.substring(i, i+1));
}
}
return buf.toString();
}
/**
* Check the estimated directionality of [str], return true if the piece of
* text should be laid out in RTL direction. If [isHtml] is true, the string
* is HTML or HTML-escaped.
*/
static bool detectRtlDirectionality(String str, {bool isHtml: false}) {
return estimateDirectionOfText(str, isHtml: isHtml) == TextDirection.RTL;
}
}
part of angular.directive;
/**
* Allows adding and removing the boolean attributes from the element.
*
* Using `<button disabled="{{false}}">` does not work since it would result
* in `<button disabled="false">` rather than `<button>`.
* Browsers change behavior based on presence/absence of attribute rather the
* its value.
*
* For this reason we provide alternate `ng-`attribute directives to
* add/remove boolean attributes such as `<button ng-disabled="{{false}}">`
* which will result in proper removal of the attribute.
*
* The full list of supported attributes are:
*
* - [ng-checked]
* - [ng-disabled]
* - [ng-multiple]
* - [ng-open]
* - [ng-readonly]
* - [ng-required]
* - [ng-selected]
*/
@NgDirective(selector: '[ng-checked]', map: const {'ng-checked': '=.checked'})
@NgDirective(selector: '[ng-disabled]', map: const {'ng-disabled': '=.disabled'})
@NgDirective(selector: '[ng-multiple]', map: const {'ng-multiple': '=.multiple'})
@NgDirective(selector: '[ng-open]', map: const {'ng-open': '=.open'})
@NgDirective(selector: '[ng-readonly]', map: const {'ng-readonly': '=.readonly'})
@NgDirective(selector: '[ng-required]', map: const {'ng-required': '=.required'})
@NgDirective(selector: '[ng-selected]', map: const {'ng-selected': '=.selected'})
class NgBooleanAttributeDirective {
NodeAttrs attrs;
NgBooleanAttributeDirective(NodeAttrs this.attrs);
_setBooleanAttribute(name, value) => attrs[name] = (toBool(value) ? '' : null);
set checked(value) => _setBooleanAttribute('checked', value);
set disabled(value) => _setBooleanAttribute('disabled', value);
set multiple(value) => _setBooleanAttribute('multiple', value);
set open(value) => _setBooleanAttribute('open', value);
set readonly(value) => _setBooleanAttribute('readonly', value);
set required(value) => _setBooleanAttribute('required', value);
set selected(value) => _setBooleanAttribute('selected', value);
}
/**
* In browser some attributes have network side-effect. If the attribute
* has `{{interpolation}}` in it it may cause browser to fetch bogus URLs.
*
* Example: In `<img src="{{username}}.png">` the browser will fetch the image
* `http://server/{{username}}.png` before Angular has a chance to replace the
* attribute with data-bound url.
*
* For this reason we provide `ng-`prefixed attributes which avoid the issues
* mentioned above as in this example: `<img ng-src="{{username}}.png">`.
*
* The full list of supported attributes are:
*
* - [ng-href]
* - [ng-src]
* - [ng-srcset]
*/
@NgDirective(selector: '[ng-href]', map: const {'ng-href': '@.href'})
@NgDirective(selector: '[ng-src]', map: const {'ng-src': '@.src'})
@NgDirective(selector: '[ng-srcset]', map: const {'ng-srcset': '@.srcset'})
class NgSourceDirective {
NodeAttrs attrs;
NgSourceDirective(NodeAttrs this.attrs);
set href(value) => attrs['href'] = value;
set src(value) => attrs['src'] = value;
set srcset(value) => attrs['srcset'] = value;
}
part of angular.directive;
/**
* The [NgTemplateElementDirective] allows one to preload an Angular template
* into the [TemplateCache]. It works on `<template>` and `<script>` elements
* that have `type="text/ng-template`. For such elements, The entire contents
* of the elements are loaded into the [TemplateCache] under the URL specified
* by the `id` attribute.
*
* Sample usage:
*
* <template id="template_1.html" type="text/ng-template">
* TEMPLATE 1 CONTENTS
* </template>
* <script id="template_2.html" type="text/ng-template">
* TEMPLATE 2 CONTENTS
* </template>
*
* Refer [TemplateCache] for a **full example** as well as more information.
*/
@NgDirective(
selector: 'template[type=text/ng-template]',
map: const {'id': '@.templateUrl'})
@NgDirective(
selector: 'script[type=text/ng-template]',
children: NgAnnotation.IGNORE_CHILDREN,
map: const {'id': '@.templateUrl'})
class NgTemplateDirective {
dom.Element element;
TemplateCache templateCache;
NgTemplateDirective(dom.Element this.element, TemplateCache this.templateCache);
set templateUrl(url) => templateCache.put(url, new HttpResponse(200,
(element.isTemplate ? element.content.innerHtml : element.innerHtml)));
}
Angular.Dart filter
===================
- [Docs](http://ci.angularjs.org/view/Dart/job/angular.dart-master/javadoc/angular.filter/FilterFilter.html)
- [source](https://github.com/angular/angular.dart/blob/master/lib/filter/filter.dart)
part of angular.filter;
/**
* Converts string to uppercase.
*
* Usage:
*
* {{ uppercase_expression | uppercase }}
*/
@NgFilter(name:'uppercase')
class UppercaseFilter {
call(String text) => text == null ? text : text.toUpperCase();
}
part of angular.directive;
/**
* Ng-model directive is responsible for reading/writing to the model.
* The directive itself is headless. (It does not know how to render or what
* events to listen for.) It is meant to be used with other directives which
* provide the rendering and listening capabilities. The directive itself
* knows how to convert the view-value into model-value and vice versa by
* allowing others to register converters (To be implemented). It also
* knwos how to (in)validate the model and the form in which it is declared
* (to be implemented)
*/
@NgDirective(
selector: '[ng-model]',
map: const {'ng-model': '&.model'})
class NgModel {
final Scope _scope;
Getter getter = ([_]) => null;
Setter setter = (_, [__]) => null;
Function _removeWatch = () => null;
bool _watchCollection;
Function render = (value) => null;
NgModel(Scope this._scope) {
watchCollection = false;
}
get watchCollection => _watchCollection;
set watchCollection(value) {
if (_watchCollection == value) return;
_watchCollection = value;
_removeWatch();
if (_watchCollection) {
_removeWatch = _scope.$watchCollection((s) => getter(), (value) => render(value) );
} else {
_removeWatch = _scope.$watch((s) => getter(), (value) => render(value) );
}
}
set model(BoundExpression boundExpression) {
getter = boundExpression;
setter = boundExpression.assign;
}
// TODO(misko): right now viewValue and modelValue are the same,
// but this needs to be changed to support converters and form validation
get viewValue => modelValue;
set viewValue(value) => modelValue = value;
get modelValue => getter();
set modelValue(value) => setter(value);
}
/**
* The UI portion of the ng-model directive. This directive registers the UI
* events and provides a rendering function for the ng-model directive.
*/
@NgDirective(selector: 'input[type=text][ng-model]')
class InputTextDirective {
dom.InputElement inputElement;
NgModel ngModel;
Scope scope;
InputTextDirective(dom.Element this.inputElement, NgModel this.ngModel, Scope this.scope) {
ngModel.render = (value) {
if (value == null) value = '';
var currentValue = inputElement.value;
if (value == currentValue) return;
var start = inputElement.selectionStart;
var end = inputElement.selectionEnd;
inputElement.value = value;
inputElement.selectionStart = start;
inputElement.selectionEnd = end;
};
inputElement.onChange.listen(relaxFnArgs(processValue));
inputElement.onKeyDown.listen((e) => new async.Timer(Duration.ZERO, processValue));
}
processValue() {
var value = inputElement.value;
if (value != ngModel.viewValue) {
scope.$apply(() => ngModel.viewValue = value);
}
}
}
/**
* The UI portion of the ng-model directive. This directive registers the UI
* events and provides a rendering function for the ng-model directive.
*/
@NgDirective(selector: 'input[type=checkbox][ng-model]')
class InputCheckboxDirective {
dom.InputElement inputElement;
NgModel ngModel;
Scope scope;
InputCheckboxDirective(dom.Element this.inputElement, NgModel this.ngModel, Scope this.scope) {
ngModel.render = (value) {
inputElement.checked = value == null ? false : toBool(value);
};
inputElement.onChange.listen((value) {
scope.$apply(() => ngModel.viewValue = inputElement.checked);
});
}
}
part of angular.directive;
// NOTE(deboer): onXX functions are now typed as 'var' instead of 'Getter'
// to work-around https://code.google.com/p/dart/issues/detail?id=13519
/**
* Allows you to specify custom behavior for DOM UI events such as mouse,
* keyboard and touch events.
*
* The custom behavior is specified via an Angular binding expression specified
* on the `ng-`*event* directive (e.g. `ng-click`). This expression is evaluated
* on the correct `scope` every time the event occurs. The event is available
* to the expression as `$event`.
*
* This is more secure than inline DOM handlers in HTML that execute arbitrary
* JavaScript code and have access to globals instead of the scope without the
* safety constraints of the Angular expression language.
*
* Example:
*
* <button ng-click="lastEvent='Click'"
* ng-doubleclick="lastEvent='DblClick'">
* Button
* </button>
*
* The full list of supported handlers are:
*
* - [ng-blur]
* - [ng-change]
* - [ng-click]
* - [ng-contextmenu]
* - [ng-doubleclick]
* - [ng-drag]
* - [ng-dragend]
* - [ng-dragenter]
* - [ng-dragleave]
* - [ng-dragover]
* - [ng-dragstart]
* - [ng-drop]
* - [ng-focus]
* - [ng-keydown]
* - [ng-keypress]
* - [ng-keyup]
* - [ng-mousedown]
* - [ng-mouseenter]
* - [ng-mouseleave]
* - [ng-mousemove]
* - [ng-mouseout]
* - [ng-mouseover]
* - [ng-mouseup]
* - [ng-mousewheel]
* - [ng-scroll]
* - [ng-touchcancel]
* - [ng-touchend]
* - [ng-touchmove]
* - [ng-touchstart]
*/
@NgDirective(selector: '[ng-blur]', map: const {'ng-blur': '&.onBlur'})
@NgDirective(selector: '[ng-change]', map: const {'ng-change': '&.onChange'})
@NgDirective(selector: '[ng-click]', map: const {'ng-click': '&.onClick'})
@NgDirective(selector: '[ng-contextmenu]', map: const {'ng-contextmenu': '&.onContextMenu'})
@NgDirective(selector: '[ng-doubleclick]', map: const {'ng-doubleclick': '&.onDoubleClick'})
@NgDirective(selector: '[ng-drag]', map: const {'ng-drag': '&.onDrag'})
@NgDirective(selector: '[ng-dragend]', map: const {'ng-dragend': '&.onDragEnd'})
@NgDirective(selector: '[ng-dragenter]', map: const {'ng-dragenter': '&.onDragEnter'})
@NgDirective(selector: '[ng-dragleave]', map: const {'ng-dragleave': '&.onDragLeave'})
@NgDirective(selector: '[ng-dragover]', map: const {'ng-dragover': '&.onDragOver'})
@NgDirective(selector: '[ng-dragstart]', map: const {'ng-dragstart': '&.onDragStart'})
@NgDirective(selector: '[ng-drop]', map: const {'ng-drop': '&.onDrop'})
@NgDirective(selector: '[ng-focus]', map: const {'ng-focus': '&.onFocus'})
@NgDirective(selector: '[ng-keydown]', map: const {'ng-keydown': '&.onKeyDown'})
@NgDirective(selector: '[ng-keypress]', map: const {'ng-keypress': '&.onKeyPress'})
@NgDirective(selector: '[ng-keyup]', map: const {'ng-keyup': '&.onKeyUp'})
@NgDirective(selector: '[ng-mousedown]', map: const {'ng-mousedown': '&.onMouseDown'})
@NgDirective(selector: '[ng-mouseenter]', map: const {'ng-mouseenter': '&.onMouseEnter'})
@NgDirective(selector: '[ng-mouseleave]', map: const {'ng-mouseleave': '&.onMouseLeave'})
@NgDirective(selector: '[ng-mousemove]', map: const {'ng-mousemove': '&.onMouseMove'})
@NgDirective(selector: '[ng-mouseout]', map: const {'ng-mouseout': '&.onMouseOut'})
@NgDirective(selector: '[ng-mouseover]', map: const {'ng-mouseover': '&.onMouseOver'})
@NgDirective(selector: '[ng-mouseup]', map: const {'ng-mouseup': '&.onMouseUp'})
@NgDirective(selector: '[ng-mousewheel]', map: const {'ng-mousewheel': '&.onMouseWheel'})
@NgDirective(selector: '[ng-scroll]', map: const {'ng-scroll': '&.onScroll'})
@NgDirective(selector: '[ng-touchcancel]', map: const {'ng-touchcancel': '&.onTouchCancel'})
@NgDirective(selector: '[ng-touchend]', map: const {'ng-touchend': '&.onTouchEnd'})
@NgDirective(selector: '[ng-touchmove]', map: const {'ng-touchmove': '&.onTouchMove'})
@NgDirective(selector: '[ng-touchstart]', map: const {'ng-touchstart': '&.onTouchStart'})
class NgEventDirective {
// NOTE: Do not use the element.on['some_event'].listen(...) syntax. Doing so
// has two downsides:
// - it loses the event typing
// - some DOM events may have multiple platform-dependent event names
// under the covers. The standard Stream getters you will get the
// platform specific event name automatically but you're on your own if
// you use the on[] syntax. This also applies to $dom_addEventListener.
// Ref: http://api.dartlang.org/docs/releases/latest/dart_html/Events.html
initListener(var stream, var handler) {
int key = stream.hashCode;
if (!listeners.containsKey(key)) {
listeners[key] = handler;
stream.listen((event) => scope.$apply(() {
handler({r"$event": event});
}));
}
}
set onBlur(value) => initListener(element.onBlur, value);
set onChange(value) => initListener(element.onChange, value);
set onClick(value) => initListener(element.onClick, value);
set onContextMenu(value) => initListener(element.onContextMenu, value);
set onDoubleClick(value) => initListener(element.onDoubleClick, value);
set onDrag(value) => initListener(element.onDrag, value);
set onDragEnd(value) => initListener(element.onDragEnd, value);
set onDragEnter(value) => initListener(element.onDragEnter, value);
set onDragLeave(value) => initListener(element.onDragLeave, value);
set onDragOver(value) => initListener(element.onDragOver, value);
set onDragStart(value) => initListener(element.onDragStart, value);
set onDrop(value) => initListener(element.onDrop, value);
set onFocus(value) => initListener(element.onFocus, value);
set onKeyDown(value) => initListener(element.onKeyDown, value);
set onKeyPress(value) => initListener(element.onKeyPress, value);
set onKeyUp(value) => initListener(element.onKeyUp, value);
set onMouseDown(value) => initListener(element.onMouseDown, value);
set onMouseEnter(value) => initListener(element.onMouseEnter, value);
set onMouseLeave(value) => initListener(element.onMouseLeave, value);
set onMouseMove(value) => initListener(element.onMouseMove, value);
set onMouseOut(value) => initListener(element.onMouseOut, value);
set onMouseOver(value) => initListener(element.onMouseOver, value);
set onMouseUp(value) => initListener(element.onMouseUp, value);
set onMouseWheel(value) => initListener(element.onMouseWheel, value);
set onScroll(value) => initListener(element.onScroll, value);
set onTouchCancel(value) => initListener(element.onTouchCancel, value);
set onTouchEnd(value) => initListener(element.onTouchEnd, value);
set onTouchMove(value) => initListener(element.onTouchMove, value);
set onTouchStart(value) => initListener(element.onTouchStart, value);
// Is it better to use a map of listeners or have 29 properties on this
// object? One would pretty much only assign to one or two of those
// properties. I'm opting for the map since it's less boilerplate code.
var listeners = {};
dom.Element element;
Scope scope;
NgEventDirective(dom.Element this.element, Scope this.scope);
}
part of angular.directive;
/**
* The ngHide directive shows or hides the given HTML element based on the
* expression provided to the ngHide attribute. The element is shown or hidden
* by changing the removing or adding the ng-hide CSS class onto the element.
*/
@NgDirective(
selector: '[ng-hide]',
map: const {'ng-hide': '=.hide'} )
class NgHideDirective {
static String NG_HIDE_CLASS = 'ng-hide';
dom.Element element;
NgHideDirective(dom.Element this.element);
set hide(value) {
if (toBool(value)) {
element.classes.add(NG_HIDE_CLASS);
} else {
element.classes.remove(NG_HIDE_CLASS);
}
}
}
/**
* The ngShow directive shows or hides the given HTML element based on the
* expression provided to the ngHide attribute. The element is shown or hidden
* by changing the removing or adding the ng-hide CSS class onto the element.
*/
@NgDirective(
selector: '[ng-show]',
map: const {'ng-show': '=.show'})
class NgShowDirective {
static String NG_SHOW_CLASS = 'ng-show';
dom.Element element;
NgShowDirective(dom.Element this.element);
set show(value) {
if (toBool(value)) {
element.classes.add(NG_SHOW_CLASS);
} else {
element.classes.remove(NG_SHOW_CLASS);
}
}
}
part of angular.core.parser;
class StaticParserFunctions {
StaticParserFunctions(Map this.functions);
Map<String, dynamic> functions;
}
class StaticParser implements Parser {
Map<String, dynamic> _functions;
Parser _fallbackParser;
StaticParser(StaticParserFunctions functions,
DynamicParser this._fallbackParser) {
assert(functions != null);
_functions = functions.functions;
}
call(String exp) {
if (exp == null) exp = "";
if (!_functions.containsKey(exp)) {
//print("Expression [$exp] is not supported in static parser");
return _fallbackParser.call(exp);
}
return _functions[exp];
}
primaryFromToken(Token token, parserError) {
throw 'Not Implemented';
}
}
part of angular.filter;
/**
* Formats a number as text.
*
* If the input is not a number an empty string is returned.
*
*
* Usage:
*
* {{ number_expression | number[:fractionSize] }}
*
*/
@NgFilter(name:'number')
class NumberFilter {
Map<num, NumberFormat> nfs = new Map<num, NumberFormat>();
/**
* [value]: the value to format
*
* [fractionSize]: Number of decimal places to round the number to. If this
* is not provided then the fraction size is computed from the current
* locale's number formatting pattern. In the case of the default locale,
* it will be 3.
*/
call(value, [fractionSize = null]) {
if (value is String) value = double.parse(value);
if (!(value is num)) return value;
if (value.isNaN) return '';
var nf = nfs[fractionSize];
if (nf == null) {
nf = new NumberFormat();
nf.maximumIntegerDigits = 9;
if (fractionSize != null) {
nf.minimumFractionDigits = fractionSize;
nf.maximumFractionDigits = fractionSize;
}
nfs[fractionSize] = nf;
}
return nf.format(value);
}
}
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
(function() {
// Bootstrap support for Dart scripts on the page as this script.
if (navigator.webkitStartDart) {
if (!navigator.webkitStartDart()) {
document.body.innerHTML = 'This build has expired. Please download a new Dartium at http://www.dartlang.org/dartium/index.html';
}
} else {
// TODO:
// - Support in-browser compilation.
// - Handle inline Dart scripts.
// Fall back to compiled JS. Run through all the scripts and
// replace them if they have a type that indicate that they source
// in Dart code.
//
// <script type="application/dart" src="..."></script>
//
var scripts = document.getElementsByTagName("script");
var length = scripts.length;
for (var i = 0; i < length; ++i) {
if (scripts[i].type == "application/dart") {
// Remap foo.dart to foo.dart.js.
if (scripts[i].src && scripts[i].src != '') {
var script = document.createElement('script');
script.src = scripts[i].src.replace(/\.dart(?=\?|$)/, '.dart.js');
var parent = scripts[i].parentNode;
// TODO(vsm): Find a solution for issue 8455 that works with more
// than one script.
document.currentScript = script;
parent.replaceChild(script, scripts[i]);
}
}
}
}
})();
part of angular.filter;
/**
* Formats date to a string based on the requested format.
* See Dart http://api.dartlang.org/docs/releases/latest/intl/DateFormat.html
* for full formating options.
*
* - `medium`: equivalent to `MMM d, y h:mm:ss a` for en_US locale (e.g. Sep 3, 2010 12:05:08 pm)
* - `short`: equivalent to `M/d/yy h:mm a` for en_US locale (e.g. 9/3/10 12:05 pm)
* - `fullDate`: equivalent to `EEEE, MMMM d, y` for en_US locale (e.g. Friday, September 3, 2010)
* - `longDate`: equivalent to `MMMM d, y` for en_US locale (e.g. September 3, 2010)
* - `mediumDate`: equivalent to `MMM d, y` for en_US locale (e.g. Sep 3, 2010)
* - `shortDate`: equivalent to `M/d/yy` for en_US locale (e.g. 9/3/10)
* - `mediumTime`: equivalent to `h:mm:ss a` for en_US locale (e.g. 12:05:08 pm)
* - `shortTime`: equivalent to `h:mm a` for en_US locale (e.g. 12:05 pm)
*
*
* Usage:
*
* {{ date_expression | date[:format] }}
*
*/
@NgFilter(name:'date')
class DateFilter {
static Map<String, String> MAP = {
'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',
};
Map<num, NumberFormat> nfs = new Map<num, NumberFormat>();
/**
* [date]: Date to format either as Date object, milliseconds
* ([string] or [num]) or various ISO 8601 datetime string formats
* (e.g. `yyyy-MM-ddTHH:mm:ss.SSSZ` and its shorter versions like
* `yyyy-MM-ddTHH:mmZ`, `yyyy-MM-dd` or `yyyyMMddTHHmmssZ`). If no
* timezone is specified in the string input, the time is considered to
* be in the local timezone.
*
* [format]: Formatting rules (see Description). If not specified,
* mediumDate is used
*
*/
call(date, [format = r'mediumDate']) {
if (date == '' || date == null) return date;
if (date is String) date = DateTime.parse(date);
if (date is num) date = new DateTime.fromMillisecondsSinceEpoch(date);
if (!(date is DateTime)) return date;
var nf = nfs[format];
if (nf == null) {
if (MAP.containsKey(format)) {
format = MAP[format];
}
nf = new DateFormat(format);
}
return nf.format(date);
}
}
library di;
export 'module.dart';
export 'injector.dart';
export 'errors.dart';
part of angular.filter;
/**
* Allows you to convert a JavaScript object into JSON string. This filter is
* mostly useful for debugging.
*
* Usage:
*
* {{ json_expression | json }}
*/
@NgFilter(name:'json')
class JsonFilter {
call(text) => stringify(text);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of intl;
/**
* Bidi stands for Bi-directional text.
* According to [Wikipedia](http://en.wikipedia.org/wiki/Bi-directional_text):
* Bi-directional text is text containing text in both text directionalities,
* both right-to-left (RTL) and left-to-right (LTR). It generally involves text
* containing different types of alphabets, but may also refer to boustrophedon,
* which is changing text directionality in each row.
*
* Utility class for formatting display text in a potentially
* opposite-directionality context without garbling layout issues.
* Mostly a very "slimmed-down" and dart-ified port of the Closure Birectional
* formatting libary. If there is a utility in the Closure library (or ICU, or
* elsewhere) that you would like this formatter to make available, please
* contact the Dart team.
*
* Provides the following functionality:
*
* 1. *BiDi Wrapping*
* When text in one language is mixed into a document in another, opposite-
* directionality language, e.g. when an English business name is embedded in a
* Hebrew web page, both the inserted string and the text following it may be
* displayed incorrectly unless the inserted string is explicitly separated
* from the surrounding text in a "wrapper" that declares its directionality at
* the start and then resets it back at the end. This wrapping can be done in
* HTML mark-up (e.g. a 'span dir=rtl' tag) or - only in contexts where mark-up
* can not be used - in Unicode BiDi formatting codes (LRE|RLE and PDF).
* Providing such wrapping services is the basic purpose of the BiDi formatter.
*
* 2. *Directionality estimation*
* How does one know whether a string about to be inserted into surrounding
* text has the same directionality? Well, in many cases, one knows that this
* must be the case when writing the code doing the insertion, e.g. when a
* localized message is inserted into a localized page. In such cases there is
* no need to involve the BiDi formatter at all. In the remaining cases, e.g.
* when the string is user-entered or comes from a database, the language of
* the string (and thus its directionality) is not known a priori, and must be
* estimated at run-time. The BiDi formatter does this automatically.
*
* 3. *Escaping*
* When wrapping plain text - i.e. text that is not already HTML or HTML-
* escaped - in HTML mark-up, the text must first be HTML-escaped to prevent XSS
* attacks and other nasty business. This of course is always true, but the
* escaping cannot be done after the string has already been wrapped in
* mark-up, so the BiDi formatter also serves as a last chance and includes
* escaping services.
*
* Thus, in a single call, the formatter will escape the input string as
* specified, determine its directionality, and wrap it as necessary. It is
* then up to the caller to insert the return value in the output.
*/
class BidiFormatter {
/** The direction of the surrounding text (the context). */
TextDirection contextDirection;
/**
* Indicates if we should always wrap the formatted text in a <span<,.
*/
bool _alwaysSpan;
/**
* Create a formatting object with a direction. If [alwaysSpan] is true we
* should always use a `span` tag, even when the input directionality is
* neutral or matches the context, so that the DOM structure of the output
* does not depend on the combination of directionalities.
*/
BidiFormatter.LTR([alwaysSpan=false]) : contextDirection = TextDirection.LTR,
_alwaysSpan = alwaysSpan;
BidiFormatter.RTL([alwaysSpan=false]) : contextDirection = TextDirection.RTL,
_alwaysSpan = alwaysSpan;
BidiFormatter.UNKNOWN([alwaysSpan=false]) :
contextDirection = TextDirection.UNKNOWN, _alwaysSpan = alwaysSpan;
/** Is true if the known context direction for this formatter is RTL. */
bool get isRTL => contextDirection == TextDirection.RTL;
/**
* Escapes HTML-special characters of [text] so that the result can be
* included verbatim in HTML source code, either in an element body or in an
* attribute value.
*
* *htmlEscape* is deprecated. Use [HtmlEscape] from the `dart:convert`
* package. *htmlEscape* will be removed the 30th of September 2013.
*/
// TODO(kevmoo) Remove this!
@deprecated
String htmlEscape(String text) => HTML_ESCAPE.convert(text);
/**
* Formats a string of a given (or estimated, if not provided)
* [direction] for use in HTML output of the context directionality, so
* an opposite-directionality string is neither garbled nor garbles what
* follows it.
* If the input string's directionality doesn't match the context
* directionality, we wrap it with a `span` tag and add a `dir` attribute
* (either "dir=rtl" or "dir=ltr").
* If alwaysSpan was true when constructing the formatter, the input is always
* wrapped with `span` tag, skipping the dir attribute when it's not needed.
*
* If [resetDir] is true and the overall directionality or the exit
* directionality of [text] is opposite to the context directionality,
* a trailing unicode BiDi mark matching the context directionality is
* appended (LRM or RLM). If [isHtml] is false, we HTML-escape the [text].
*/
String wrapWithSpan(String text, {bool isHtml: false, bool resetDir: true,
TextDirection direction}) {
if (direction == null) direction = estimateDirection(text, isHtml: isHtml);
var result;
if (!isHtml) text = HTML_ESCAPE.convert(text);
var directionChange = contextDirection.isDirectionChange(direction);
if (_alwaysSpan || directionChange) {
var spanDirection = '';
if (directionChange) {
spanDirection = ' dir=${direction.spanText}';
}
result= '<span$spanDirection>$text</span>';
} else {
result = text;
}
return result + (resetDir? _resetDir(text, direction, isHtml) : '');
}
/**
* Format [text] of a known (if specified) or estimated [direction] for use
* in *plain-text* output of the context directionality, so an
* opposite-directionality text is neither garbled nor garbles what follows
* it. Unlike wrapWithSpan, this makes use of unicode BiDi formatting
* characters instead of spans for wrapping. The returned string would be
* RLE+text+PDF for RTL text, or LRE+text+PDF for LTR text.
*
* If [resetDir] is true, and if the overall directionality or the exit
* directionality of text are opposite to the context directionality,
* a trailing unicode BiDi mark matching the context directionality is
* appended (LRM or RLM).
*
* In HTML, the *only* valid use of this function is inside of elements that
* do not allow markup, e.g. an 'option' tag.
* This function does *not* do HTML-escaping regardless of the value of
* [isHtml]. [isHtml] is used to designate if the text contains HTML (escaped
* or unescaped).
*/
String wrapWithUnicode(String text, {bool isHtml: false, bool resetDir: true,
TextDirection direction}) {
if (direction == null) direction = estimateDirection(text, isHtml: isHtml);
var result = text;
if (contextDirection.isDirectionChange(direction)) {
var marker = direction == TextDirection.RTL ? Bidi.RLE : Bidi.LRE;
result = "${marker}$text${Bidi.PDF}";
}
return result + (resetDir? _resetDir(text, direction, isHtml) : '');
}
/**
* Estimates the directionality of [text] using the best known
* general-purpose method (using relative word counts). A
* TextDirection.UNKNOWN return value indicates completely neutral input.
* [isHtml] is true if [text] HTML or HTML-escaped.
*/
TextDirection estimateDirection(String text, {bool isHtml: false}) {
return Bidi.estimateDirectionOfText(text, isHtml: isHtml); //TODO~!!!
}
/**
* Returns a unicode BiDi mark matching the surrounding context's [direction]
* (not necessarily the direction of [text]). The function returns an LRM or
* RLM if the overall directionality or the exit directionality of [text] is
* opposite the context directionality. Otherwise
* return the empty string. [isHtml] is true if [text] is HTML or
* HTML-escaped.
*/
String _resetDir(String text, TextDirection direction, bool isHtml) {
// endsWithRtl and endsWithLtr are called only if needed (short-circuit).
if ((contextDirection == TextDirection.LTR &&
(direction == TextDirection.RTL ||
Bidi.endsWithRtl(text, isHtml))) ||
(contextDirection == TextDirection.RTL &&
(direction == TextDirection.LTR ||
Bidi.endsWithLtr(text, isHtml)))) {
if (contextDirection == TextDirection.LTR) {
return Bidi.LRM;
} else {
return Bidi.RLM;
}
} else {
return '';
}
}
}
part of angular.core.dom;
class NullTreeSanitizer implements dom.NodeTreeSanitizer {
void sanitizeTree(dom.Node node) {}
}
part of angular.core.dom;
/**
* BoundBlockFactory is a [BlockFactory] which does not need Injector because
* it is pre-bound to an injector from the parent. This means that this
* BoundBlockFactory can only be used from within a specific Directive such
* as [NgRepeat], but it can not be stored in a cache.
*
* The BoundBlockFactory needs [Scope] to be created.
*/
class BoundBlockFactory {
BlockFactory blockFactory;
Injector injector;
BoundBlockFactory(BlockFactory this.blockFactory, Injector this.injector);
Block call(Scope scope) {
return blockFactory(injector.createChild([new Module()..value(Scope, scope)]));
}
}
/**
* BlockFactory is used to create new [Block]s. BlockFactory is created by the
* [Compiler] as a result of compiling a template.
*/
class BlockFactory {
List directivePositions;
List<dom.Node> templateElements;
Profiler _perf;
BlockFactory(this.templateElements, this.directivePositions, this._perf);
BoundBlockFactory bind(Injector injector) {
return new BoundBlockFactory(this, injector);
}
Block call(Injector injector, [List<dom.Node> elements]) {
if (elements == null) {
elements = cloneElements(templateElements);
}
var block = new Block(elements);
_link(block, elements, directivePositions, injector);
return block;
}
_link(Block block, List<dom.Node> nodeList, List directivePositions, Injector parentInjector) {
var preRenderedIndexOffset = 0;
var directiveDefsByName = {
};
for (num i = 0, ii = directivePositions.length; i < ii;) {
num index = directivePositions[i++];
List<DirectiveRef> directiveRefs = directivePositions[i++];
List childDirectivePositions = directivePositions[i++];
var nodeListIndex = index + preRenderedIndexOffset;
dom.Node node = nodeList[nodeListIndex];
// if node isn't attached to the DOM, create a parent for it.
var parentNode = node.parentNode;
var fakeParent = false;
if (parentNode == null) {
fakeParent = true;
parentNode = new dom.DivElement();
parentNode.append(node);
}
var childInjector = _instantiateDirectives(block, parentInjector, node,
directiveRefs, parentInjector.get(Parser));
if (childDirectivePositions != null) {
_link(block, node.nodes, childDirectivePositions, childInjector);
}
if (fakeParent) {
// extract the node from the parentNode.
nodeList[nodeListIndex] = parentNode.nodes[0];
}
}
}
Injector _instantiateDirectives(Block block, Injector parentInjector,
dom.Node node, List<DirectiveRef> directiveRefs,
Parser parser) =>
_perf.time('angular.blockFactory.instantiateDirectives', () {
if (directiveRefs == null || directiveRefs.length == 0) return parentInjector;
var nodeModule = new Module();
var blockHoleFactory = (_) => null;
var blockFactory = (_) => null;
var boundBlockFactory = (_) => null;
var nodeAttrs = node is dom.Element ? new NodeAttrs(node) : null;
var nodesAttrsDirectives = null;
Map<Type, _ComponentFactory> fctrs;
nodeModule.value(Block, block);
nodeModule.value(dom.Element, node);
nodeModule.value(dom.Node, node);
nodeModule.value(NodeAttrs, nodeAttrs);
directiveRefs.forEach((DirectiveRef ref) {
NgAnnotation annotation = ref.annotation;
var visibility = _elementOnly;
if (ref.annotation.visibility == NgDirective.CHILDREN_VISIBILITY) {
visibility = null;
} else if (ref.annotation.visibility == NgDirective.DIRECT_CHILDREN_VISIBILITY) {
visibility = _elementDirectChildren;
}
if (ref.type == NgTextMustacheDirective) {
nodeModule.factory(NgTextMustacheDirective, (Injector injector) {
return new NgTextMustacheDirective(
node, ref.value, injector.get(Interpolate), injector.get(Scope),
injector.get(TextChangeListener));
});
} else if (ref.type == NgAttrMustacheDirective) {
if (nodesAttrsDirectives == null) {
nodesAttrsDirectives = [];
nodeModule.factory(NgAttrMustacheDirective, (Injector injector) {
nodesAttrsDirectives.forEach((ref) {
new NgAttrMustacheDirective(nodeAttrs, ref.value, injector.get(Interpolate), injector.get(Scope));
});
});
}
nodesAttrsDirectives.add(ref);
} else if (ref.annotation is NgComponent) {
//nodeModule.factory(type, new ComponentFactory(node, ref.directive), visibility: visibility);
// TODO(misko): there should be no need to wrap function like this.
nodeModule.factory(ref.type, (Injector injector) {
Compiler compiler = injector.get(Compiler);
Scope scope = injector.get(Scope);
BlockCache blockCache = injector.get(BlockCache);
Http http = injector.get(Http);
TemplateCache templateCache = injector.get(TemplateCache);
// This is a bit of a hack since we are returning different type then we are.
var componentFactory = new _ComponentFactory(node, ref.type, ref.annotation as NgComponent, injector.get(dom.NodeTreeSanitizer));
if (fctrs == null) fctrs = new Map<Type, _ComponentFactory>();
fctrs[ref.type] = componentFactory;
return componentFactory(injector, compiler, scope, blockCache, http, templateCache);
}, visibility: visibility);
} else {
nodeModule.type(ref.type, visibility: visibility);
}
for (var publishType in ref.annotation.publishTypes) {
nodeModule.factory(publishType, (Injector injector) => injector.get(ref.type), visibility: visibility);
}
if (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) {
// Currently, transclude is only supported for NgDirective.
assert(annotation is NgDirective);
blockHoleFactory = (_) => new BlockHole([node]);
blockFactory = (_) => ref.blockFactory;
boundBlockFactory = (Injector injector) => ref.blockFactory.bind(injector);
}
});
nodeModule.factory(BlockHole, blockHoleFactory);
nodeModule.factory(BlockFactory, blockFactory);
nodeModule.factory(BoundBlockFactory, boundBlockFactory);
var nodeInjector = parentInjector.createChild([nodeModule]);
var scope = nodeInjector.get(Scope);
directiveRefs.forEach((ref) {
var controller = nodeInjector.get(ref.type);
var shadowScope = (fctrs != null && fctrs.containsKey(ref.type)) ? fctrs[ref.type].shadowScope : null;
if (ref.annotation.publishAs != null) {
(shadowScope == null ? scope : shadowScope)[ref.annotation.publishAs] = controller;
}
_createAttributeMapping(ref.annotation, nodeAttrs == null ? new _AnchorAttrs(ref) : nodeAttrs, scope, shadowScope, controller, parser);
if (controller is NgAttachAware) {
var removeWatcher;
removeWatcher = scope.$watch(() {
removeWatcher();
controller.attach();
});
}
if (controller is NgDetachAware) {
scope.$on(r'$destroy', controller.detach);
}
});
return nodeInjector;
});
// DI visibility callback allowing node-local visibility.
bool _elementOnly(Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
return identical(requesting, defining);
}
// DI visibility callback allowing visibility from direct child into parent.
bool _elementDirectChildren(Injector requesting, Injector defining) {
if (requesting.name == _SHADOW) {
requesting = requesting.parent;
}
return _elementOnly(requesting, defining) || identical(requesting.parent, defining);
}
}
/**
* BlockCache is used to cache the compilation of templates into [Block]s.
* It can be used synchronously if HTML is known or asynchronously if the
* template HTML needs to be looked up from the URL.
*/
class BlockCache {
// _blockFactoryCache is unbounded
Cache<String, BlockFactory> _blockFactoryCache =
new LruCache<String, BlockFactory>(capacity: 0);
Http $http;
TemplateCache $templateCache;
Compiler compiler;
dom.NodeTreeSanitizer treeSanitizer;
BlockCache(Http this.$http, TemplateCache this.$templateCache, Compiler this.compiler, dom.NodeTreeSanitizer this.treeSanitizer);
BlockFactory fromHtml(String html) {
BlockFactory blockFactory = _blockFactoryCache.get(html);
if (blockFactory == null) {
var div = new dom.Element.tag('div');
div.setInnerHtml(html, treeSanitizer: treeSanitizer);
blockFactory = compiler(div.nodes);
_blockFactoryCache.put(html, blockFactory);
}
return blockFactory;
}
async.Future<BlockFactory> fromUrl(String url) {
return $http.getString(url, cache: $templateCache).then((String tmpl) {
return fromHtml(tmpl);
});
}
}
/**
* ComponentFactory is responsible for setting up components. This includes
* the shadowDom, fetching template, importing styles, setting up attribute
* mappings, publishing the controller, and compiling and caching the template.
*/
class _ComponentFactory {
final dom.Element element;
final Type type;
final NgComponent component;
final dom.NodeTreeSanitizer treeSanitizer;
dom.ShadowRoot shadowDom;
Scope shadowScope;
Injector shadowInjector;
Compiler compiler;
var controller;
_ComponentFactory(this.element, Type this.type, NgComponent this.component, this.treeSanitizer);
dynamic call(Injector injector, Compiler compiler, Scope scope, BlockCache $blockCache, Http $http, TemplateCache $templateCache) {
this.compiler = compiler;
shadowDom = element.createShadowRoot();
shadowDom.applyAuthorStyles = component.applyAuthorStyles;
shadowDom.resetStyleInheritance = component.resetStyleInheritance;
shadowScope = scope.$new(true);
// TODO(pavelgj): fetching CSS with Http is mainly an attempt to
// work around an unfiled Chrome bug when reloading same CSS breaks
// styles all over the page. We shouldn't be doing browsers work,
// so change back to using @import once Chrome bug is fixed or a
// better work around is found.
async.Future<String> cssFuture;
if (component.cssUrl != null) {
cssFuture = $http.getString(component.cssUrl, cache: $templateCache);
} else {
cssFuture = new async.Future.value(null);
}
var blockFuture;
if (component.template != null) {
blockFuture = new async.Future.value($blockCache.fromHtml(component.template));
} else if (component.templateUrl != null) {
blockFuture = $blockCache.fromUrl(component.templateUrl);
}
TemplateLoader templateLoader = new TemplateLoader(cssFuture.then((String css) {
if (css != null) {
shadowDom.setInnerHtml('<style>$css</style>', treeSanitizer: treeSanitizer);
}
if (blockFuture != null) {
return blockFuture.then((BlockFactory blockFactory) => attachBlockToShadowDom(blockFactory));
}
return shadowDom;
}));
controller = createShadowInjector(injector, templateLoader).get(type);
return controller;
}
attachBlockToShadowDom(BlockFactory blockFactory) {
var block = blockFactory(shadowInjector);
shadowDom.nodes.addAll(block.elements);
return shadowDom;
}
createShadowInjector(injector, TemplateLoader templateLoader) {
var shadowModule = new Module()
..type(type)
..value(Scope, shadowScope)
..value(TemplateLoader, templateLoader)
..value(dom.ShadowRoot, shadowDom);
shadowInjector = injector.createChild([shadowModule], name: _SHADOW);
return shadowInjector;
}
}
class _AnchorAttrs extends NodeAttrs {
DirectiveRef _directiveRef;
_AnchorAttrs(DirectiveRef this._directiveRef):super(null);
operator [](name) => name == '.' ? _directiveRef.value : null;
observe(String attributeName, AttributeChanged notifyFn) {
if (attributeName == '.') {
notifyFn(_directiveRef.value);
} else {
notifyFn(null);
}
}
}
RegExp _MAPPING = new RegExp(r'^([\@\=\&\!])(\.?)\s*(.*)$');
_createAttributeMapping(NgAnnotation annotation, NodeAttrs nodeAttrs,
Scope scope, Scope shadowScope, Object controller, Parser parser) {
if (annotation.map != null) annotation.map.forEach((attrName, mapping) {
Match match = _MAPPING.firstMatch(mapping);
if (match == null) {
throw "Unknown mapping '$mapping' for attribute '$attrName'.";
}
var mode = match[1];
var controllerContext = match[2];
var dstPath = match[3];
var context = controllerContext == '.' ? controller : shadowScope;
Expression dstPathFn = parser(dstPath.isEmpty ? attrName : dstPath);
if (!dstPathFn.assignable) {
throw "Expression '$dstPath' is not assignable in mapping '$mapping' for attribute '$attrName'.";
}
switch (mode) {
case '@':
nodeAttrs.observe(attrName, (value) => dstPathFn.assign(context, value));
break;
case '=':
Expression attrExprFn = parser(nodeAttrs[attrName]);
var shadowValue = null;
scope.$watch(() => attrExprFn.eval(scope), (v) => dstPathFn.assign(context, shadowValue = v), nodeAttrs[attrName]);
if (shadowScope != null) {
if (attrExprFn.assignable) {
shadowScope.$watch(() => dstPathFn.eval(context), (v) {
if (shadowValue != v) {
shadowValue = v;
attrExprFn.assign(scope, v);
}
});
}
}
break;
case '!':
Expression attrExprFn = parser(nodeAttrs[attrName]);
var stopWatching;
stopWatching = scope.$watch(() => attrExprFn.eval(scope), (value) {
if (dstPathFn.assign(context, value) != null) {
stopWatching();
}
}, nodeAttrs[attrName]);
break;
case '&':
dstPathFn.assign(context, parser(nodeAttrs[attrName]).bind(scope));
break;
}
});
}
bool _understands(obj, symbol) {
if (symbol is String) symbol = new Symbol(symbol);
return reflect(obj).type.methods.containsKey(symbol);
}
String _SHADOW = 'SHADOW_INJECTOR';
part of angular.core.parser;
class BoundExpression {
var _context;
Expression expression;
BoundExpression(this._context, Expression this.expression);
call([locals]) => expression.eval(_context, locals);
assign(value, [locals]) => expression.assign(_context, value, locals);
}
class Expression implements ParserAST {
final ParsedGetter eval;
final ParsedSetter assign;
String exp;
List parts;
Expression(ParsedGetter this.eval, [ParsedSetter this.assign]);
bind(context) => new BoundExpression(context, this);
get assignable => assign != null;
}
class GetterSetter {
static stripTrailingNulls(List l) {
while (l.length > 0 && l.last == null) {
l.removeLast();
}
return l;
}
_maybeInvoke(instanceMirror, symbol) {
if (instanceMirror.type.members.containsKey(symbol)) {
MethodMirror methodMirror = instanceMirror.type.members[symbol];
return relaxFnArgs(([a0, a1, a2, a3, a4, a5]) {
var args = stripTrailingNulls([a0, a1, a2, a3, a4, a5]);
return instanceMirror.invoke(symbol, args).reflectee;
});
}
return null;
}
Function getter(String key) {
var symbol = new Symbol(key);
return (o) {
InstanceMirror instanceMirror = reflect(o);
try {
return instanceMirror.getField(symbol).reflectee;
} on NoSuchMethodError catch (e) {
var invokeClosure = _maybeInvoke(instanceMirror, symbol);
if (invokeClosure == null) {
rethrow;
}
return invokeClosure;
} on UnsupportedError catch (e) {
var invokeClosure = _maybeInvoke(instanceMirror, symbol);
if (invokeClosure == null) {
rethrow;
}
return invokeClosure;
}
};
}
Function setter(String key) {
var symbol = new Symbol(key);
return (o, v) {
reflect(o).setField(symbol, v);
return v;
};
}
}
var undefined_ = const Symbol("UNDEFINED");
class ParserBackend {
GetterSetter _getterSetter;
FilterMap _filters;
ParserBackend(GetterSetter this._getterSetter, FilterMap this._filters);
static Expression ZERO = new Expression((_, [_x]) => 0);
getter(String path) {
List<String> keys = path.split('.');
List<Function> getters = keys.map(_getterSetter.getter).toList();
if (getters.isEmpty) {
return (self, [locals]) => self;
} else {
return (dynamic self, [Map locals]) {
if (self == null) {
return null;
}
// Cache for local closure access
List<String> _keys = keys;
List<Function> _getters = getters;
var _gettersLength = _getters.length;
num i = 0;
if (locals != null) {
dynamic selfNext = locals[_keys[0]];
if (selfNext == null) {
if (locals.containsKey(_keys[0])) {
return null;
}
} else {
i++;
self = selfNext;
}
}
for (; i < _gettersLength; i++) {
if (self is Map) {
self = self[_keys[i]];
} else {
self = _getters[i](self);
}
if (self == null) {
return null;
}
}
return self;
};
}
}
setter(String path) {
List<String> keys = path.split('.');
List<Function> getters = keys.map(_getterSetter.getter).toList();
List<Function> setters = keys.map(_getterSetter.setter).toList();
return (dynamic self, dynamic value, [Map locals]) {
num i = 0;
List<String> _keys = keys;
List<Function> _getters = getters;
List<Function> _setters = setters;
var setterLengthMinusOne = _keys.length - 1;
dynamic selfNext;
if (locals != null && i < setterLengthMinusOne) {
selfNext = locals[_keys[0]];
if (selfNext == null) {
if (locals.containsKey(_keys[0])) {
return null;
}
} else {
i++;
self = selfNext;
}
}
for (; i < setterLengthMinusOne; i++) {
if (self is Map) {
selfNext = self[_keys[i]];
} else {
selfNext = _getters[i](self);
}
if (selfNext == null) {
selfNext = {};
if (self is Map) {
self[_keys[i]] = selfNext;
} else {
_setters[i](self, selfNext);
}
}
self = selfNext;
}
if (self is Map) {
self[_keys[setterLengthMinusOne]] = value;
} else {
_setters[i](self, value);
}
return value;
};
}
_op(opKey) => OPERATORS[opKey];
Expression binaryFn(Expression left, String op, Expression right) =>
new Expression((self, [locals]) => _op(op)(self, locals, left, right));
Expression unaryFn(String op, Expression right) =>
new Expression((self, [locals]) => _op(op)(self, locals, right, null));
Expression assignment(Expression left, Expression right, evalError) =>
new Expression((self, [locals]) {
try {
return left.assign(self, right.eval(self, locals), locals);
} catch (e, s) {
throw evalError('Caught $e', s);
}
});
Expression multipleStatements(statements) =>
new Expression((self, [locals]) {
var value;
for ( var i = 0; i < statements.length; i++) {
var statement = statements[i];
if (statement != null)
value = statement.eval(self, locals);
}
return value;
});
Expression functionCall(fn, fnName, argsFn, evalError) =>
new Expression((self, [locals]){
List args = [];
for ( var i = 0; i < argsFn.length; i++) {
args.add(argsFn[i].eval(self, locals));
}
var userFn = safeFunctionCall(fn.eval(self, locals), fnName, evalError);
return relaxFnApply(userFn, args);
});
Expression arrayDeclaration(elementFns) =>
new Expression((self, [locals]){
var array = [];
for ( var i = 0; i < elementFns.length; i++) {
array.add(elementFns[i].eval(self, locals));
}
return array;
});
Expression objectIndex(obj, indexFn, evalError) =>
new Expression((self, [locals]) {
var i = indexFn.eval(self, locals);
var o = obj.eval(self, locals),
v, p;
v = objectIndexGetField(o, i, evalError);
return v;
}, (self, value, [locals]) =>
objectIndexSetField(obj.eval(self, locals),
indexFn.eval(self, locals), value, evalError)
);
Expression fieldAccess(object, field) {
var setterFn = setter(field);
var getterFn = getter(field);
return new Expression(
(self, [locals]) => getterFn(object.eval(self, locals)),
(self, value, [locals]) => setterFn(object.eval(self, locals), value));
}
Expression object(keyValues) =>
new Expression((self, [locals]){
var object = {};
for ( var i = 0; i < keyValues.length; i++) {
var keyValue = keyValues[i];
var value = keyValue["value"].eval(self, locals);
object[keyValue["key"]] = value;
}
return object;
});
Expression profiled(value, _perf, text) {
if (value is FilterExpression) return value;
var wrappedGetter = (s, [l]) =>
_perf.time('angular.parser.getter', () => value.eval(s, l), text);
var wrappedAssignFn = null;
if (value.assign != null) {
wrappedAssignFn = (s, v, [l]) =>
_perf.time('angular.parser.assign',
() => value.assign(s, v, l), text);
}
return new Expression(wrappedGetter, wrappedAssignFn);
}
Expression fromOperator(String op) =>
new Expression((s, [l]) => OPERATORS[op](s, l, null, null));
Expression getterSetter(key) =>
new Expression(getter(key), setter(key));
Expression value(v) =>
new Expression((self, [locals]) => v);
zero() => ZERO;
FilterExpression filter(String filterName,
Expression leftHandSide,
List<Expression> parameters,
Function evalError) {
var filterFn = _filters(filterName);
return new FilterExpression(filterFn, leftHandSide, parameters);
}
}
class FilterExpression extends Expression {
final Function filterFn;
final Expression leftHandSide;
final List<Expression> parameters;
FilterExpression(Function this.filterFn,
Expression this.leftHandSide,
List<Expression> this.parameters): super(null);
get eval => _eval;
dynamic _eval(self, [locals]) {
var args = [leftHandSide.eval(self, locals)];
for ( var i = 0; i < parameters.length; i++) {
args.add(parameters[i].eval(self, locals));
}
return Function.apply(filterFn, args);
}
}
part of angular.core.dom;
class Compiler {
DirectiveMap directives;
DirectiveSelector selector;
Profiler _perf;
Compiler(DirectiveMap this.directives, Profiler this._perf) {
selector = directiveSelectorFactory(directives);
}
_compileBlock(NodeCursor domCursor, NodeCursor templateCursor,
List<DirectiveRef> useExistingDirectiveRefs) {
if (domCursor.nodeList().length == 0) return null;
var directivePositions = null; // don't pre-create to create sparse tree and prevent GC pressure.
var cursorAlreadyAdvanced;
do {
var declaredDirectiveRefs = useExistingDirectiveRefs == null
? selector(domCursor.nodeList()[0])
: useExistingDirectiveRefs;
var children = NgAnnotation.COMPILE_CHILDREN;
var childDirectivePositions = null;
List<DirectiveRef> usableDirectiveRefs = null;
cursorAlreadyAdvanced = false;
for (var j = 0, jj = declaredDirectiveRefs.length; j < jj; j++) {
DirectiveRef directiveRef = declaredDirectiveRefs[j];
NgAnnotation annotation = directiveRef.annotation;
var blockFactory = null;
if (annotation.children != children &&
children == NgAnnotation.COMPILE_CHILDREN) {
children = annotation.children;
}
if (children == NgAnnotation.TRANSCLUDE_CHILDREN) {
var remainingDirectives = declaredDirectiveRefs.sublist(j + 1);
blockFactory = compileTransclusion(
domCursor, templateCursor,
directiveRef, remainingDirectives);
j = jj; // stop processing further directives since they belong to transclusion;
}
if (usableDirectiveRefs == null) {
usableDirectiveRefs = [];
}
directiveRef.blockFactory = blockFactory;
usableDirectiveRefs.add(directiveRef);
}
if (children == NgAnnotation.COMPILE_CHILDREN && domCursor.descend()) {
templateCursor.descend();
childDirectivePositions = _compileBlock(domCursor, templateCursor, null);
domCursor.ascend();
templateCursor.ascend();
}
if (childDirectivePositions != null || usableDirectiveRefs != null) {
if (directivePositions == null) directivePositions = [];
var directiveOffsetIndex = templateCursor.index;
directivePositions
..add(directiveOffsetIndex)
..add(usableDirectiveRefs)
..add(childDirectivePositions);
}
} while (templateCursor.microNext() && domCursor.microNext());
return directivePositions;
}
BlockFactory compileTransclusion(
NodeCursor domCursor, NodeCursor templateCursor,
DirectiveRef directiveRef,
List<DirectiveRef> transcludedDirectiveRefs) {
var anchorName = directiveRef.annotation.selector + (directiveRef.value != null ? '=' + directiveRef.value : '');
var blockFactory;
var blocks;
var transcludeCursor = templateCursor.replaceWithAnchor(anchorName);
var domCursorIndex = domCursor.index;
var directivePositions = _compileBlock(domCursor, transcludeCursor, transcludedDirectiveRefs);
if (directivePositions == null) directivePositions = [];
blockFactory = new BlockFactory(transcludeCursor.elements, directivePositions, _perf);
domCursor.index = domCursorIndex;
if (domCursor.isInstance()) {
domCursor.insertAnchorBefore(anchorName);
blocks = [blockFactory(domCursor.nodeList())];
domCursor.macroNext();
templateCursor.macroNext();
while (domCursor.isValid() && domCursor.isInstance()) {
blocks.add(blockFactory(domCursor.nodeList()));
domCursor.macroNext();
templateCursor.remove();
}
} else {
domCursor.replaceWithAnchor(anchorName);
}
return blockFactory;
}
BlockFactory call(List<dom.Node> elements) => _perf.time('angular.compiler', () {
List<dom.Node> domElements = elements;
List<dom.Node> templateElements = cloneElements(domElements);
var directivePositions = _compileBlock(
new NodeCursor(domElements), new NodeCursor(templateElements),
null);
return new BlockFactory(templateElements,
directivePositions == null ? [] : directivePositions, _perf);
});
}
library angular.io;
typedef FsVisitor(String file);
/**
* A simple mockabe wrapper around dart:io that can be used without introducing
* direct dependencies on dart:io.
*/
abstract class IoService {
String readAsStringSync(String filePath);
void visitFs(String rootDir, FsVisitor visitor);
}
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* This contains internal implementation details of the date formatting code
* which are exposed as public functions because they must be called by other
* libraries in order to configure the source for the locale data. We don't want
* them exposed as public API functions in the date formatting library, so they
* are put in a separate library here. These are for internal use only. User
* code should import one of the `date_symbol_data...` libraries and call the
* `initializeDateFormatting` method exposed there.
*/
library date_format_internal;
import 'dart:async';
import 'intl_helpers.dart';
import '../date_symbols.dart';
/**
* This holds the symbols to be used for date/time formatting, indexed
* by locale. Note that it will be set differently during initialization,
* depending on what implementation we are using. By default, it is initialized
* to an instance of UninitializedLocaleData, so any attempt to use it will
* result in an informative error message.
*/
var dateTimeSymbols =
new UninitializedLocaleData('initializeDateFormatting(<locale>)',
en_USSymbols);
/**
* This holds the patterns used for date/time formatting, indexed
* by locale. Note that it will be set differently during initialization,
* depending on what implementation we are using. By default, it is initialized
* to an instance of UninitializedLocaleData, so any attempt to use it will
* result in an informative error message.
*/
var dateTimePatterns =
new UninitializedLocaleData('initializeDateFormatting(<locale>)',
en_USPatterns);
/**
* Initialize the symbols dictionary. This should be passed a function that
* creates and returns the symbol data. We take a function so that if
* initializing the data is an expensive operation it need only be done once,
* no matter how many times this method is called.
*/
void initializeDateSymbols(Function symbols) {
if (dateTimeSymbols is UninitializedLocaleData) {
dateTimeSymbols = symbols();
}
}
/**
* Initialize the patterns dictionary. This should be passed a function that
* creates and returns the pattern data. We take a function so that if
* initializing the data is an expensive operation it need only be done once,
* no matter how many times this method is called.
*/
void initializeDatePatterns(Function patterns) {
if (dateTimePatterns is UninitializedLocaleData) {
dateTimePatterns = patterns();
}
}
Future initializeIndividualLocaleDateFormatting(Function init) {
return init(dateTimeSymbols, dateTimePatterns);
}
part of angular.core;
/**
* Any uncaught exception in angular expressions is delegated to this service.
* The default implementation logs exceptions into console.
*
* In your application it is expected that this service is overridden with
* your implementation which can store the exception for later processing.
*/
class ExceptionHandler {
/**
* Delegate uncaught exception for central error handling.
*
* - [error] The error which was caught.
* - [stack] The stacktrace.
* - [reason] Optional contextual information for the error.
*/
call(dynamic error, dynamic stack, [String reason = '']){
print("$error\n$reason\nSTACKTRACE:\n$stack");
}
}
part of angular.mock;
/**
* Mock implementation of [ExceptionHandler] that rethrows exceptions.
*/
class RethrowExceptionHandler extends ExceptionHandler {
call(error, stack, [reason]){
throw "$error $reason \nORIGINAL STACKTRACE:\n $stack";
}
}
class ExceptionWithStack {
final dynamic error;
final dynamic stack;
ExceptionWithStack(dynamic this.error, dynamic this.stack);
toString() => "$error\n$stack";
}
/**
* Mock implementation of [ExceptionHandler] that logs all exceptions for
* later processing.
*/
class LoggingExceptionHandler implements ExceptionHandler {
/**
* All exceptions are stored here for later examining.
*/
final List<ExceptionWithStack> errors = [];
call(error, stack, [reason]) {
errors.add(new ExceptionWithStack(error, stack));
}
/**
* This method throws an exception if the errors is not empty.
* It is recommended that this method is called on test tear-down
* to verify that all exceptions have been processed.
*/
assertEmpty() {
if (errors.length > 0) {
throw new ArgumentError('Exception Logger not empty:\n$errors');
}
}
}
library angular.filter;
import 'dart:json';
import 'package:intl/intl.dart';
import 'package:di/di.dart';
import '../core/module.dart';
import '../core/parser/parser_library.dart';
part 'currency.dart';
part 'date.dart';
part 'filter.dart';
part 'json.dart';
part 'limit_to.dart';
part 'lowercase.dart';
part 'number.dart';
part 'order_by.dart';
part 'uppercase.dart';
class NgFilterModule extends Module {
NgFilterModule() {
type(CurrencyFilter);
type(DateFilter);
type(FilterFilter);
type(JsonFilter);
type(LimitToFilter);
type(LowercaseFilter);
type(NumberFilter);
type(OrderByFilter);
type(UppercaseFilter);
}
}
part of angular.mock;
String depth = '';
ENTER(name) {
dump('${depth}ENTER: $name');
depth = depth + ' ';
}
LEAVE(name) {
depth = depth.substring(0, depth.length -2);
dump('${depth}LEAVE: $name');
}
MARK(name) {
dump('$depth$name');
}
dump([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10]) {
var log = [];
if (p1 != null) log.add(STRINGIFY(p1));
if (p2 != null) log.add(STRINGIFY(p2));
if (p3 != null) log.add(STRINGIFY(p3));
if (p4 != null) log.add(STRINGIFY(p4));
if (p5 != null) log.add(STRINGIFY(p5));
if (p6 != null) log.add(STRINGIFY(p6));
if (p7 != null) log.add(STRINGIFY(p7));
if (p8 != null) log.add(STRINGIFY(p8));
if (p9 != null) log.add(STRINGIFY(p9));
if (p10 != null) log.add(STRINGIFY(p10));
js.scoped(() {
(js.context as dynamic).console.log(log.join(', '));
});
}
STRINGIFY(obj) {
if (obj is List) {
var out = [];
obj.forEach((i) => out.add(STRINGIFY(i)));
return '[${out.join(", ")}]';
} else if (obj is Comment) {
return '<!--${obj.text}-->';
} else if (obj is Element) {
return obj.outerHtml;
} else if (obj is String) {
return '"$obj"';
} else {
return obj.toString();
}
}
part of angular.core;
String _startSymbol = '{{';
String _endSymbol = '}}';
num _startSymbolLength = _startSymbol.length;
num _endSymbolLength = _endSymbol.length;
/**
* Compiles a string with markup into an interpolation function. This service
* is used by the HTML [Compiler] service for data binding.
*
*
* var $interpolate = ...; // injected
* var exp = $interpolate('Hello {{name}}!');
* expect(exp({name:'Angular'}).toEqual('Hello Angular!');
*/
class Interpolate {
Parser _parse;
ExceptionHandler _exceptionHandler;
Interpolate(Parser this._parse, ExceptionHandler this._exceptionHandler);
/**
* Compile markup text into interpolation function.
*
* - `text`: The markup text to interpolate in form `foo {{expr}} bar`.
* - `mustHaveExpression`: if set to true then the interpolation string must
* have embedded expression in order to return an interpolation function.
* Strings with no embedded expression will return null for the
* interpolation function.
*/
Expression call(String text, [bool mustHaveExpression = false]) {
num startIndex;
num endIndex;
num index = 0;
List chunks = [];
num length = text.length;
bool hasInterpolation = false;
String exp;
List concat = [];
Expression fn;
while(index < length) {
if ( ((startIndex = text.indexOf(_startSymbol, index)) != -1) &&
((endIndex = text.indexOf(_endSymbol, startIndex + _startSymbolLength)) != -1) ) {
if (index != startIndex) {
chunks.add(text.substring(index, startIndex));
}
fn = _parse(exp = text.substring(startIndex + _startSymbolLength, endIndex));
chunks.add(fn);
fn.exp = exp;
index = endIndex + _endSymbolLength;
hasInterpolation = true;
} else {
// we did not find anything, so we have to add the remainder to the chunks array
if (index != length) {
chunks.add(text.substring(index));
}
index = length;
}
}
if ((length = chunks.length) == 0) {
// we added, nothing, must have been an empty string.
chunks.add('');
length = 1;
}
if (!mustHaveExpression || hasInterpolation) {
fn = new Expression((context, [locals]) {
try {
for(var i = 0, ii = length, chunk; i<ii; i++) {
if ((chunk = chunks[i]) is Expression) {
chunk = chunk.eval(context);
if (chunk == null) {
chunk = '';
} else if (!(chunk is String)) {
chunk = '$chunk';
}
}
concat.add(chunk);
}
return concat.join('');
} catch(err, s) {
_exceptionHandler("\$interpolate error! Can't interpolate: $text\n$err", s);
} finally {
concat.length = 0;
}
});
fn.exp = text;
fn.parts = chunks;
return fn;
}
}
}