Adding mapbox-gl branch

This commit is contained in:
Andreas Hocevar
2015-03-16 18:50:27 +01:00
parent 7985f030fa
commit 57ee7f52fd
3109 changed files with 943365 additions and 0 deletions
@@ -0,0 +1,182 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.Const');
goog.require('goog.asserts');
goog.require('goog.string.TypedString');
/**
* Wrapper for compile-time-constant strings.
*
* Const is a wrapper for strings that can only be created from program
* constants (i.e., string literals). This property relies on a custom Closure
* compiler check that {@code goog.string.Const.from} is only invoked on
* compile-time-constant expressions.
*
* Const is useful in APIs whose correct and secure use requires that certain
* arguments are not attacker controlled: Compile-time constants are inherently
* under the control of the application and not under control of external
* attackers, and hence are safe to use in such contexts.
*
* Instances of this type must be created via its factory method
* {@code goog.string.Const.from} and not by invoking its constructor. The
* constructor intentionally takes no parameters and the type is immutable;
* hence only a default instance corresponding to the empty string can be
* obtained via constructor invocation.
*
* @see goog.string.Const#from
* @constructor
* @final
* @struct
* @implements {goog.string.TypedString}
*/
goog.string.Const = function() {
/**
* The wrapped value of this Const object. The field has a purposely ugly
* name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.string.Const#unwrap
* @const
* @private
*/
this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
goog.string.Const.TYPE_MARKER_;
};
/**
* @override
* @const
*/
goog.string.Const.prototype.implementsGoogStringTypedString = true;
/**
* Returns this Const's value a string.
*
* IMPORTANT: In code where it is security-relevant that an object's type is
* indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap}
* instead of this method.
*
* @see goog.string.Const#unwrap
* @override
*/
goog.string.Const.prototype.getTypedStringValue = function() {
return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
};
/**
* Returns a debug-string representation of this value.
*
* To obtain the actual string value wrapped inside an object of this type,
* use {@code goog.string.Const.unwrap}.
*
* @see goog.string.Const#unwrap
* @override
*/
goog.string.Const.prototype.toString = function() {
return 'Const{' +
this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
'}';
};
/**
* Performs a runtime check that the provided object is indeed an instance
* of {@code goog.string.Const}, and returns its value.
* @param {!goog.string.Const} stringConst The object to extract from.
* @return {string} The Const object's contained string, unless the run-time
* type check fails. In that case, {@code unwrap} returns an innocuous
* string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.string.Const.unwrap = function(stringConst) {
// Perform additional run-time type-checking to ensure that stringConst is
// indeed an instance of the expected type. This provides some additional
// protection against security bugs due to application code that disables type
// checks.
if (stringConst instanceof goog.string.Const &&
stringConst.constructor === goog.string.Const &&
stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
goog.string.Const.TYPE_MARKER_) {
return stringConst.
stringConstValueWithSecurityContract__googStringSecurityPrivate_;
} else {
goog.asserts.fail('expected object of type Const, got \'' +
stringConst + '\'');
return 'type_error:Const';
}
};
/**
* Creates a Const object from a compile-time constant string.
*
* It is illegal to invoke this function on an expression whose
* compile-time-contant value cannot be determined by the Closure compiler.
*
* Correct invocations include,
* <pre>
* var s = goog.string.Const.from('hello');
* var t = goog.string.Const.from('hello' + 'world');
* </pre>
*
* In contrast, the following are illegal:
* <pre>
* var s = goog.string.Const.from(getHello());
* var t = goog.string.Const.from('hello' + world);
* </pre>
*
* TODO(user): Compile-time checks that this function is only called
* with compile-time constant expressions.
*
* @param {string} s A constant string from which to create a Const.
* @return {!goog.string.Const} A Const object initialized to stringConst.
*/
goog.string.Const.from = function(s) {
return goog.string.Const.create__googStringSecurityPrivate_(s);
};
/**
* Type marker for the Const type, used to implement additional run-time
* type checking.
* @const
* @private
*/
goog.string.Const.TYPE_MARKER_ = {};
/**
* Utility method to create Const instances.
* @param {string} s The string to initialize the Const object with.
* @return {!goog.string.Const} The initialized Const object.
* @private
*/
goog.string.Const.create__googStringSecurityPrivate_ = function(s) {
var stringConst = new goog.string.Const();
stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
s;
return stringConst;
};
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2006 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.string</title>
<script src="../base.js"></script>
<script>
goog.require('goog.string.constTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,51 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Unit tests for goog.string.Const.
*/
goog.provide('goog.string.constTest');
goog.require('goog.string.Const');
goog.require('goog.testing.jsunit');
goog.setTestOnly('goog.string.constTest');
function testConst() {
var constString = goog.string.Const.from('blah');
var extracted = goog.string.Const.unwrap(constString);
assertEquals('blah', extracted);
assertEquals('blah', constString.getTypedStringValue());
assertEquals('Const{blah}', String(constString));
// Interface marker is present.
assertTrue(constString.implementsGoogStringTypedString);
}
/** @suppress {checkTypes} */
function testUnwrap() {
var evil = {};
evil.constStringValueWithSecurityContract__googStringSecurityPrivate_ =
'evil';
evil.CONST_STRING_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ = {};
var exception = assertThrows(function() {
goog.string.Const.unwrap(evil);
});
assertTrue(exception.message.indexOf('expected object of type Const') > 0);
}
@@ -0,0 +1,252 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility function for linkifying text.
* @author bolinfest@google.com (Michael Bolin)
*/
goog.provide('goog.string.linkify');
goog.require('goog.string');
/**
* Takes a string of plain text and linkifies URLs and email addresses. For a
* URL (unless opt_attributes is specified), the target of the link will be
* _blank and it will have a rel=nofollow attribute applied to it so that links
* created by linkify will not be of interest to search engines.
* @param {string} text Plain text.
* @param {Object<string, string>=} opt_attributes Attributes to add to all
* links created. Default are rel=nofollow and target=_blank. To clear
* those default attributes set rel='' and target=''.
* @return {string} HTML Linkified HTML text. Any text that is not part of a
* link will be HTML-escaped.
*/
goog.string.linkify.linkifyPlainText = function(text, opt_attributes) {
// This shortcut makes linkifyPlainText ~10x faster if text doesn't contain
// URLs or email addresses and adds insignificant performance penalty if it
// does.
if (text.indexOf('@') == -1 &&
text.indexOf('://') == -1 &&
text.indexOf('www.') == -1 &&
text.indexOf('Www.') == -1 &&
text.indexOf('WWW.') == -1) {
return goog.string.htmlEscape(text);
}
var attributesMap = opt_attributes || {};
// Set default options.
if (!('rel' in attributesMap)) {
attributesMap['rel'] = 'nofollow';
}
if (!('target' in attributesMap)) {
attributesMap['target'] = '_blank';
}
// Creates attributes string from options.
var attributesArray = [];
for (var key in attributesMap) {
if (attributesMap.hasOwnProperty(key) && attributesMap[key]) {
attributesArray.push(
goog.string.htmlEscape(key), '="',
goog.string.htmlEscape(attributesMap[key]), '" ');
}
}
var attributes = attributesArray.join('');
return text.replace(
goog.string.linkify.FIND_LINKS_RE_,
function(part, before, original, email, protocol) {
var output = [goog.string.htmlEscape(before)];
if (!original) {
return output[0];
}
output.push('<a ', attributes, 'href="');
/** @type {string} */
var linkText;
/** @type {string} */
var afterLink;
if (email) {
output.push('mailto:');
linkText = email;
afterLink = '';
} else {
// This is a full url link.
if (!protocol) {
output.push('http://');
}
var splitEndingPunctuation =
original.match(goog.string.linkify.ENDS_WITH_PUNCTUATION_RE_);
// An open paren in the link will often be matched with a close paren
// at the end, so skip cutting off ending punctuation if there's an
// open paren. For example:
// http://en.wikipedia.org/wiki/Titanic_(1997_film)
if (splitEndingPunctuation && !goog.string.contains(original, '(')) {
linkText = splitEndingPunctuation[1];
afterLink = splitEndingPunctuation[2];
} else {
linkText = original;
afterLink = '';
}
}
linkText = goog.string.htmlEscape(linkText);
afterLink = goog.string.htmlEscape(afterLink);
output.push(linkText, '">', linkText, '</a>', afterLink);
return output.join('');
});
};
/**
* Gets the first URI in text.
* @param {string} text Plain text.
* @return {string} The first URL, or an empty string if not found.
*/
goog.string.linkify.findFirstUrl = function(text) {
var link = text.match(goog.string.linkify.URL_);
return link != null ? link[0] : '';
};
/**
* Gets the first email address in text.
* @param {string} text Plain text.
* @return {string} The first email address, or an empty string if not found.
*/
goog.string.linkify.findFirstEmail = function(text) {
var email = text.match(goog.string.linkify.EMAIL_);
return email != null ? email[0] : '';
};
/**
* If a series of these characters is at the end of a url, it will be considered
* punctuation and not part of the url.
* @type {string}
* @const
* @private
*/
goog.string.linkify.ENDING_PUNCTUATION_CHARS_ = ':;,\\.?>\\]\\)!';
/**
* @type {!RegExp}
* @const
* @private
*/
goog.string.linkify.ENDS_WITH_PUNCTUATION_RE_ = new RegExp(
'^(.*?)([' + goog.string.linkify.ENDING_PUNCTUATION_CHARS_ + ']+)$');
/**
* Set of characters to be put into a regex character set ("[...]"), used to
* match against a url hostname and everything after it. It includes
* "#-@", which represents the characters "#$%&'()*+,-./0123456789:;<=>?@".
* @type {string}
* @const
* @private
*/
goog.string.linkify.ACCEPTABLE_URL_CHARS_ = '\\w~#-@!\\[\\]';
/**
* List of all protocols patterns recognized in urls (mailto is handled in email
* matching).
* @type {!Array<string>}
* @const
* @private
*/
goog.string.linkify.RECOGNIZED_PROTOCOLS_ = ['https?', 'ftp'];
/**
* Regular expression pattern that matches the beginning of an url.
* Contains a catching group to capture the scheme.
* @type {string}
* @const
* @private
*/
goog.string.linkify.PROTOCOL_START_ =
'(' + goog.string.linkify.RECOGNIZED_PROTOCOLS_.join('|') + ')://';
/**
* Regular expression pattern that matches the beginning of a typical
* http url without the http:// scheme.
* @type {string}
* @const
* @private
*/
goog.string.linkify.WWW_START_ = 'www\\.';
/**
* Regular expression pattern that matches an url.
* @type {string}
* @const
* @private
*/
goog.string.linkify.URL_ =
'(?:' + goog.string.linkify.PROTOCOL_START_ + '|' +
goog.string.linkify.WWW_START_ + ')\\w[' +
goog.string.linkify.ACCEPTABLE_URL_CHARS_ + ']*';
/**
* Regular expression pattern that matches a top level domain.
* @type {string}
* @const
* @private
*/
goog.string.linkify.TOP_LEVEL_DOMAIN_ =
'(?:com|org|net|edu|gov' +
// from http://www.iana.org/gtld/gtld.htm
'|aero|biz|cat|coop|info|int|jobs|mobi|museum|name|pro|travel' +
'|arpa|asia|xxx' +
// a two letter country code
'|[a-z][a-z])\\b';
/**
* Regular expression pattern that matches an email.
* Contains a catching group to capture the email without the optional "mailto:"
* prefix.
* @type {string}
* @const
* @private
*/
goog.string.linkify.EMAIL_ =
'(?:mailto:)?([\\w.+-]+@[A-Za-z0-9.-]+\\.' +
goog.string.linkify.TOP_LEVEL_DOMAIN_ + ')';
/**
* Regular expression to match all the links (url or email) in a string.
* First match is text before first link, might be empty string.
* Second match is the original text that should be replaced by a link.
* Third match is the email address in the case of an email.
* Fourth match is the scheme of the url if specified.
* @type {!RegExp}
* @const
* @private
*/
goog.string.linkify.FIND_LINKS_RE_ = new RegExp(
// Match everything including newlines.
'([\\S\\s]*?)(' +
// Match email after a word break.
'\\b' + goog.string.linkify.EMAIL_ + '|' +
// Match url after a workd break.
'\\b' + goog.string.linkify.URL_ + '|$)',
'gi');
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>
Closure Unit Tests - goog.string.linkify
</title>
<script src="../base.js">
</script>
<script>
goog.require('goog.string.linkifyTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,455 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.linkifyTest');
goog.setTestOnly('goog.string.linkifyTest');
goog.require('goog.string');
goog.require('goog.string.linkify');
goog.require('goog.testing.dom');
goog.require('goog.testing.jsunit');
var div = document.createElement('div');
function assertLinkify(comment, input, expected) {
assertEquals(
comment, expected,
goog.string.linkify.linkifyPlainText(input, {rel: '', target: ''}));
}
function testContainsNoLink() {
assertLinkify(
'Text does not contain any links',
'Text with no links in it.',
'Text with no links in it.');
}
function testContainsALink() {
assertLinkify(
'Text only contains a link',
'http://www.google.com/',
'<a href="http://www.google.com/">http://www.google.com/<\/a>');
}
function testStartsWithALink() {
assertLinkify(
'Text starts with a link',
'http://www.google.com/ is a well known search engine',
'<a href="http://www.google.com/">http://www.google.com/<\/a>' +
' is a well known search engine');
}
function testEndsWithALink() {
assertLinkify(
'Text ends with a link',
'Look at this search engine: http://www.google.com/',
'Look at this search engine: ' +
'<a href="http://www.google.com/">http://www.google.com/<\/a>');
}
function testContainsOnlyEmail() {
assertLinkify(
'Text only contains an email address',
'bolinfest@google.com',
'<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>');
}
function testStartsWithAnEmail() {
assertLinkify(
'Text starts with an email address',
'bolinfest@google.com wrote this test.',
'<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>' +
' wrote this test.');
}
function testEndsWithAnEmail() {
assertLinkify(
'Text ends with an email address',
'This test was written by bolinfest@google.com.',
'This test was written by ' +
'<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>.');
}
function testUrlWithPortNumber() {
assertLinkify(
'URL with a port number',
'http://www.google.com:80/',
'<a href="http://www.google.com:80/">http://www.google.com:80/<\/a>');
}
function testUrlWithUserPasswordAndPortNumber() {
assertLinkify(
'URL with a user, a password and a port number',
'http://lascap:p4ssw0rd@google.com:80/s?q=a&hl=en',
'<a href="http://lascap:p4ssw0rd@google.com:80/s?q=a&amp;hl=en">' +
'http://lascap:p4ssw0rd@google.com:80/s?q=a&amp;hl=en<\/a>');
}
function testUrlWithUnderscore() {
assertLinkify(
'URL with an underscore',
'http://www_foo.google.com/',
'<a href="http://www_foo.google.com/">http://www_foo.google.com/<\/a>');
}
function testInternalUrlWithoutDomain() {
assertLinkify(
'Internal URL without a proper domain',
'http://tracker/1068594',
'<a href="http://tracker/1068594">http://tracker/1068594<\/a>');
}
function testInternalUrlOneChar() {
assertLinkify(
'Internal URL with a one char domain',
'http://b',
'<a href="http://b">http://b<\/a>');
}
function testSecureInternalUrlWithoutDomain() {
assertLinkify(
'Secure Internal URL without a proper domain',
'https://review/6594805',
'<a href="https://review/6594805">https://review/6594805<\/a>');
}
function testTwoUrls() {
assertLinkify(
'Text with two URLs in it',
'I use both http://www.google.com and http://yahoo.com, don\'t you?',
'I use both <a href="http://www.google.com">http://www.google.com<\/a> ' +
'and <a href="http://yahoo.com">http://yahoo.com<\/a>, ' +
goog.string.htmlEscape('don\'t you?'));
}
function testGetParams() {
assertLinkify(
'URL with GET params',
'http://google.com/?a=b&c=d&e=f',
'<a href="http://google.com/?a=b&amp;c=d&amp;e=f">' +
'http://google.com/?a=b&amp;c=d&amp;e=f<\/a>');
}
function testGoogleCache() {
assertLinkify(
'Google search result from cache',
'http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
'www.google.com/intl/en/help/features.html+google+cache&hl=en',
'<a href="http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
'www.google.com/intl/en/help/features.html+google+cache&amp;hl=en">' +
'http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
'www.google.com/intl/en/help/features.html+google+cache&amp;hl=en' +
'<\/a>');
}
function testUrlWithoutHttp() {
assertLinkify(
'URL without http protocol',
'It\'s faster to type www.google.com without the http:// in front.',
goog.string.htmlEscape('It\'s faster to type ') +
'<a href="http://www.google.com">www.google.com' +
'<\/a> without the http:// in front.');
}
function testUrlWithCapitalsWithoutHttp() {
assertLinkify(
'URL with capital letters without http protocol',
'It\'s faster to type Www.google.com without the http:// in front.',
goog.string.htmlEscape('It\'s faster to type ') +
'<a href="http://Www.google.com">Www.google.com' +
'<\/a> without the http:// in front.');
}
function testUrlHashBang() {
assertLinkify(
'URL with #!',
'Another test URL: ' +
'https://www.google.com/testurls/#!/page',
'Another test URL: ' +
'<a href="https://www.google.com/testurls/#!/page">' +
'https://www.google.com/testurls/#!/page<\/a>');
}
function testTextLooksLikeUrlWithoutHttp() {
assertLinkify(
'Text looks like an url but is not',
'This showww.is just great: www.is',
'This showww.is just great: <a href="http://www.is">www.is<\/a>');
}
function testEmailWithSubdomain() {
assertLinkify(
'Email with a subdomain',
'Send mail to bolinfest@groups.google.com.',
'Send mail to <a href="mailto:bolinfest@groups.google.com">' +
'bolinfest@groups.google.com<\/a>.');
}
function testEmailWithHyphen() {
assertLinkify(
'Email with a hyphen in the domain name',
'Send mail to bolinfest@google-groups.com.',
'Send mail to <a href="mailto:bolinfest@google-groups.com">' +
'bolinfest@google-groups.com<\/a>.');
}
function testEmailUsernameWithSpecialChars() {
assertLinkify(
'Email with a hyphen, period, and + in the user name',
'Send mail to bolin-fest+for.um@google.com',
'Send mail to <a href="mailto:bolin-fest+for.um@google.com">' +
'bolin-fest+for.um@google.com<\/a>');
}
function testEmailWithUnderscoreInvalid() {
assertLinkify(
'Email with an underscore in the domain name, which is invalid',
'Do not email bolinfest@google_groups.com.',
'Do not email bolinfest@google_groups.com.');
}
function testUrlNotHttp() {
assertLinkify(
'Url using unusual scheme',
'Looking for some goodies: ftp://ftp.google.com/goodstuff/',
'Looking for some goodies: ' +
'<a href="ftp://ftp.google.com/goodstuff/">' +
'ftp://ftp.google.com/goodstuff/<\/a>');
}
function testJsInjection() {
assertLinkify(
'Text includes some javascript',
'Welcome in hell <script>alert(\'this is hell\')<\/script>',
goog.string.htmlEscape(
'Welcome in hell <script>alert(\'this is hell\')<\/script>'));
}
function testJsInjectionDotIsBlind() {
assertLinkify(
'Javascript injection using regex . blindness to newline chars',
'<script>malicious_code()<\/script>\nVery nice url: www.google.com',
'&lt;script&gt;malicious_code()&lt;/script&gt;\nVery nice url: ' +
'<a href="http://www.google.com">www.google.com<\/a>');
}
function testJsInjectionWithUnicodeLineReturn() {
assertLinkify(
'Javascript injection using regex . blindness to newline chars with a ' +
'unicode newline character.',
'<script>malicious_code()<\/script>\u2029Vanilla text',
'&lt;script&gt;malicious_code()&lt;/script&gt;\u2029Vanilla text');
}
function testJsInjectionWithIgnorableNonTagChar() {
assertLinkify(
'Angle brackets are normalized even when followed by an ignorable ' +
'non-tag character.',
'<\u0000img onerror=alert(1337) src=\n>',
'&lt;&#0;img onerror=alert(1337) src=\n&gt;');
}
function testJsInjectionWithTextarea() {
assertLinkify(
'Putting the result in a textarea can\'t cause other textarea text to ' +
'be treated as tag content.',
'</textarea',
'&lt;/textarea');
}
function testJsInjectionWithNewlineConversion() {
assertLinkify(
'Any newline conversion and whitespace normalization won\'t cause tag ' +
'parts to be recombined.',
'<<br>script<br>>alert(1337)<<br>/<br>script<br>>',
'&lt;&lt;br&gt;script&lt;br&gt;&gt;alert(1337)&lt;&lt;br&gt;/&lt;' +
'br&gt;script&lt;br&gt;&gt;');
}
function testNoProtocolBlacklisting() {
assertLinkify(
'No protocol blacklisting.',
'Click: jscript:alert%281337%29\nClick: JSscript:alert%281337%29\n' +
'Click: VBscript:alert%281337%29\nClick: Script:alert%281337%29\n' +
'Click: flavascript:alert%281337%29',
'Click: jscript:alert%281337%29\nClick: JSscript:alert%281337%29\n' +
'Click: VBscript:alert%281337%29\nClick: Script:alert%281337%29\n' +
'Click: flavascript:alert%281337%29');
}
function testProtocolWhitelistingEffective() {
assertLinkify(
'Protocol whitelisting is effective.',
'Click httpscript:alert%281337%29\nClick mailtoscript:alert%281337%29\n' +
'Click j\u00A0avascript:alert%281337%29\n' +
'Click \u00A0javascript:alert%281337%29',
'Click httpscript:alert%281337%29\nClick mailtoscript:alert%281337%29\n' +
'Click j\u00A0avascript:alert%281337%29\n' +
'Click \u00A0javascript:alert%281337%29');
}
function testLinkifyNoOptions() {
div.innerHTML = goog.string.linkify.linkifyPlainText('http://www.google.com');
goog.testing.dom.assertHtmlContentsMatch(
'<a href="http://www.google.com" target="_blank" rel="nofollow">' +
'http://www.google.com<\/a>',
div, true /* opt_strictAttributes */);
}
function testLinkifyOptionsNoAttributes() {
div.innerHTML = goog.string.linkify.linkifyPlainText(
'The link for www.google.com is located somewhere in ' +
'https://www.google.fr/?hl=en, you should find it easily.',
{rel: '', target: ''});
goog.testing.dom.assertHtmlContentsMatch(
'The link for <a href="http://www.google.com">www.google.com<\/a> is ' +
'located somewhere in ' +
'<a href="https://www.google.fr/?hl=en">https://www.google.fr/?hl=en' +
'<\/a>, you should find it easily.',
div, true /* opt_strictAttributes */);
}
function testLinkifyOptionsClassName() {
div.innerHTML = goog.string.linkify.linkifyPlainText(
'Attribute with <class> name www.w3c.org.',
{'class': 'link-added'});
goog.testing.dom.assertHtmlContentsMatch(
'Attribute with &lt;class&gt; name <a href="http://www.w3c.org" ' +
'target="_blank" rel="nofollow" class="link-added">www.w3c.org<\/a>.',
div, true /* opt_strictAttributes */);
}
function testFindFirstUrlNoScheme() {
assertEquals('www.google.com', goog.string.linkify.findFirstUrl(
'www.google.com'));
}
function testFindFirstUrlNoSchemeWithText() {
assertEquals('www.google.com', goog.string.linkify.findFirstUrl(
'prefix www.google.com something'));
}
function testFindFirstUrlScheme() {
assertEquals('http://www.google.com', goog.string.linkify.findFirstUrl(
'http://www.google.com'));
}
function testFindFirstUrlSchemeWithText() {
assertEquals('http://www.google.com', goog.string.linkify.findFirstUrl(
'prefix http://www.google.com something'));
}
function testFindFirstUrlNoUrl() {
assertEquals('', goog.string.linkify.findFirstUrl(
'ygvtfr676 5v68fk uygbt85F^&%^&I%FVvc .'));
}
function testFindFirstEmailNoScheme() {
assertEquals('fake@google.com', goog.string.linkify.findFirstEmail(
'fake@google.com'));
}
function testFindFirstEmailNoSchemeWithText() {
assertEquals('fake@google.com', goog.string.linkify.findFirstEmail(
'prefix fake@google.com something'));
}
function testFindFirstEmailScheme() {
assertEquals('mailto:fake@google.com', goog.string.linkify.findFirstEmail(
'mailto:fake@google.com'));
}
function testFindFirstEmailSchemeWithText() {
assertEquals('mailto:fake@google.com', goog.string.linkify.findFirstEmail(
'prefix mailto:fake@google.com something'));
}
function testFindFirstEmailNoUrl() {
assertEquals('', goog.string.linkify.findFirstEmail(
'ygvtfr676 5v68fk uygbt85F^&%^&I%FVvc .'));
}
function testContainsPunctuation_parens() {
assertLinkify(
'Link contains parens, but does not end with them',
'www.google.com/abc(v1).html',
'<a href="http://www.google.com/abc(v1).html">' +
'www.google.com/abc(v1).html<\/a>');
}
function testEndsWithPunctuation() {
assertLinkify(
'Link ends with punctuation',
'Have you seen www.google.com? It\'s awesome.',
'Have you seen <a href="http://www.google.com">www.google.com<\/a>?' +
goog.string.htmlEscape(' It\'s awesome.'));
}
function testEndsWithPunctuation_closeParen() {
assertLinkify(
'Link inside parentheses',
'(For more info see www.googl.com)',
'(For more info see <a href="http://www.googl.com">www.googl.com<\/a>)');
assertLinkify(
'Parentheses inside link',
'http://en.wikipedia.org/wiki/Titanic_(1997_film)',
'<a href="http://en.wikipedia.org/wiki/Titanic_(1997_film)">' +
'http://en.wikipedia.org/wiki/Titanic_(1997_film)<\/a>');
}
function testEndsWithPunctuation_openParen() {
assertLinkify(
'Link followed by open parenthesis',
'www.google.com(',
'<a href="http://www.google.com(">www.google.com(<\/a>');
}
function testEndsWithPunctuation_angles() {
assertLinkify(
'Link inside angled brackets',
'Here is a bibliography entry <http://www.google.com/>',
'Here is a bibliography entry &lt;<a href="http://www.google.com/">' +
'http://www.google.com/<\/a>&gt;');
}
function testEndsWithPunctuation_closingPairThenSingle() {
assertLinkify(
'Link followed by closing punctuation pair then singular punctuation',
'Here is a bibliography entry <http://www.google.com/>, PTAL.',
'Here is a bibliography entry &lt;<a href="http://www.google.com/">' +
'http://www.google.com/<\/a>&gt;, PTAL.');
}
function testEndsWithPunctuation_ellipses() {
assertLinkify(
'Link followed by three dots',
'just look it up on www.google.com...',
'just look it up on <a href="http://www.google.com">www.google.com' +
'<\/a>...');
}
function testBracketsInUrl() {
assertLinkify(
'Link containing brackets',
'before http://google.com/details?answer[0]=42 after',
'before <a href="http://google.com/details?answer[0]=42">' +
'http://google.com/details?answer[0]=42<\/a> after');
}
function testUrlWithExclamation() {
assertLinkify(
'URL with exclamation points',
'This is awesome www.google.com!',
'This is awesome <a href="http://www.google.com">www.google.com<\/a>!');
}
@@ -0,0 +1,154 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities for string newlines.
* @author nnaze@google.com (Nathan Naze)
*/
/**
* Namespace for string utilities
*/
goog.provide('goog.string.newlines');
goog.provide('goog.string.newlines.Line');
goog.require('goog.array');
/**
* Splits a string into lines, properly handling universal newlines.
* @param {string} str String to split.
* @param {boolean=} opt_keepNewlines Whether to keep the newlines in the
* resulting strings. Defaults to false.
* @return {!Array<string>} String split into lines.
*/
goog.string.newlines.splitLines = function(str, opt_keepNewlines) {
var lines = goog.string.newlines.getLines(str);
return goog.array.map(lines, function(line) {
return opt_keepNewlines ? line.getFullLine() : line.getContent();
});
};
/**
* Line metadata class that records the start/end indicies of lines
* in a string. Can be used to implement common newline use cases such as
* splitLines() or determining line/column of an index in a string.
* Also implements methods to get line contents.
*
* Indexes are expressed as string indicies into string.substring(), inclusive
* at the start, exclusive at the end.
*
* Create an array of these with goog.string.newlines.getLines().
* @param {string} string The original string.
* @param {number} startLineIndex The index of the start of the line.
* @param {number} endContentIndex The index of the end of the line, excluding
* newlines.
* @param {number} endLineIndex The index of the end of the line, index
* newlines.
* @constructor
* @struct
* @final
*/
goog.string.newlines.Line = function(string, startLineIndex,
endContentIndex, endLineIndex) {
/**
* The original string.
* @type {string}
*/
this.string = string;
/**
* Index of the start of the line.
* @type {number}
*/
this.startLineIndex = startLineIndex;
/**
* Index of the end of the line, excluding any newline characters.
* Index is the first character after the line, suitable for
* String.substring().
* @type {number}
*/
this.endContentIndex = endContentIndex;
/**
* Index of the end of the line, excluding any newline characters.
* Index is the first character after the line, suitable for
* String.substring().
* @type {number}
*/
this.endLineIndex = endLineIndex;
};
/**
* @return {string} The content of the line, excluding any newline characters.
*/
goog.string.newlines.Line.prototype.getContent = function() {
return this.string.substring(this.startLineIndex, this.endContentIndex);
};
/**
* @return {string} The full line, including any newline characters.
*/
goog.string.newlines.Line.prototype.getFullLine = function() {
return this.string.substring(this.startLineIndex, this.endLineIndex);
};
/**
* @return {string} The newline characters, if any ('\n', \r', '\r\n', '', etc).
*/
goog.string.newlines.Line.prototype.getNewline = function() {
return this.string.substring(this.endContentIndex, this.endLineIndex);
};
/**
* Splits a string into an array of line metadata.
* @param {string} str String to split.
* @return {!Array<!goog.string.newlines.Line>} Array of line metadata.
*/
goog.string.newlines.getLines = function(str) {
// We use the constructor because literals are evaluated only once in
// < ES 3.1.
// See http://www.mail-archive.com/es-discuss@mozilla.org/msg01796.html
var re = RegExp('\r\n|\r|\n', 'g');
var sliceIndex = 0;
var result;
var lines = [];
while (result = re.exec(str)) {
var line = new goog.string.newlines.Line(
str, sliceIndex, result.index, result.index + result[0].length);
lines.push(line);
// remember where to start the slice from
sliceIndex = re.lastIndex;
}
// If the string does not end with a newline, add the last line.
if (sliceIndex < str.length) {
var line = new goog.string.newlines.Line(
str, sliceIndex, str.length, str.length);
lines.push(line);
}
return lines;
};
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2006 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.string</title>
<script src="../base.js"></script>
<script>
goog.require('goog.string.newlinesTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,87 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Unit tests for goog.string.
*/
goog.provide('goog.string.newlinesTest');
goog.require('goog.string.newlines');
goog.require('goog.testing.jsunit');
goog.setTestOnly('goog.string.newlinesTest');
// test for goog.string.splitLines
function testSplitLines() {
/**
* @param {!Array<string>} expected
* @param {string} string
* @param {boolean=} opt_keepNewlines
*/
function assertSplitLines(expected, string, opt_keepNewlines) {
var keepNewlines = opt_keepNewlines || false;
var lines = goog.string.newlines.splitLines(string, keepNewlines);
assertElementsEquals(expected, lines);
}
// Test values borrowed from Python's splitlines. http://goo.gl/iwawx
assertSplitLines(['abc', 'def', '', 'ghi'], 'abc\ndef\n\rghi');
assertSplitLines(['abc', 'def', '', 'ghi'], 'abc\ndef\n\r\nghi');
assertSplitLines(['abc', 'def', 'ghi'], 'abc\ndef\r\nghi');
assertSplitLines(['abc', 'def', 'ghi'], 'abc\ndef\r\nghi\n');
assertSplitLines(['abc', 'def', 'ghi', ''], 'abc\ndef\r\nghi\n\r');
assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
assertSplitLines(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
'\nabc\ndef\r\nghi\n\r', true);
assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
assertSplitLines(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
'\nabc\ndef\r\nghi\n\r', true);
}
function testGetLines() {
var lines = goog.string.newlines.getLines('abc\ndef\n\rghi');
assertEquals(4, lines.length);
assertEquals(0, lines[0].startLineIndex);
assertEquals(3, lines[0].endContentIndex);
assertEquals(4, lines[0].endLineIndex);
assertEquals('abc', lines[0].getContent());
assertEquals('abc\n', lines[0].getFullLine());
assertEquals('\n', lines[0].getNewline());
assertEquals(4, lines[1].startLineIndex);
assertEquals(7, lines[1].endContentIndex);
assertEquals(8, lines[1].endLineIndex);
assertEquals('def', lines[1].getContent());
assertEquals('def\n', lines[1].getFullLine());
assertEquals('\n', lines[1].getNewline());
assertEquals(8, lines[2].startLineIndex);
assertEquals(8, lines[2].endContentIndex);
assertEquals(9, lines[2].endLineIndex);
assertEquals('', lines[2].getContent());
assertEquals('\r', lines[2].getFullLine());
assertEquals('\r', lines[2].getNewline());
assertEquals(9, lines[3].startLineIndex);
assertEquals(12, lines[3].endContentIndex);
assertEquals(12, lines[3].endLineIndex);
assertEquals('ghi', lines[3].getContent());
assertEquals('ghi', lines[3].getFullLine());
assertEquals('', lines[3].getNewline());
}
@@ -0,0 +1,38 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines an interface for parsing strings into objects.
*/
goog.provide('goog.string.Parser');
/**
* An interface for parsing strings into objects.
* @interface
*/
goog.string.Parser = function() {};
/**
* Parses a string into an object and returns the result.
* Agnostic to the format of string and object.
*
* @param {string} s The string to parse.
* @return {*} The object generated from the string.
*/
goog.string.Parser.prototype.parse;
@@ -0,0 +1,169 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities for dealing with POSIX path strings. Based on
* Python's os.path and posixpath.
* @author nnaze@google.com (Nathan Naze)
*/
goog.provide('goog.string.path');
goog.require('goog.array');
goog.require('goog.string');
/**
* Returns the final component of a pathname.
* See http://docs.python.org/library/os.path.html#os.path.basename
* @param {string} path A pathname.
* @return {string} path The final component of a pathname, i.e. everything
* after the final slash.
*/
goog.string.path.baseName = function(path) {
var i = path.lastIndexOf('/') + 1;
return path.slice(i);
};
/**
* Alias to goog.string.path.baseName.
* @param {string} path A pathname.
* @return {string} path The final component of a pathname.
* @deprecated Use goog.string.path.baseName.
*/
goog.string.path.basename = goog.string.path.baseName;
/**
* Returns the directory component of a pathname.
* See http://docs.python.org/library/os.path.html#os.path.dirname
* @param {string} path A pathname.
* @return {string} The directory component of a pathname, i.e. everything
* leading up to the final slash.
*/
goog.string.path.dirname = function(path) {
var i = path.lastIndexOf('/') + 1;
var head = path.slice(0, i);
// If the path isn't all forward slashes, trim the trailing slashes.
if (!/^\/+$/.test(head)) {
head = head.replace(/\/+$/, '');
}
return head;
};
/**
* Extracts the extension part of a pathname.
* @param {string} path The path name to process.
* @return {string} The extension if any, otherwise the empty string.
*/
goog.string.path.extension = function(path) {
var separator = '.';
// Combining all adjacent periods in the basename to a single period.
var baseName = goog.string.path.baseName(path).replace(/\.+/g, separator);
var separatorIndex = baseName.lastIndexOf(separator);
return separatorIndex <= 0 ? '' : baseName.substr(separatorIndex + 1);
};
/**
* Joins one or more path components (e.g. 'foo/' and 'bar' make 'foo/bar').
* An absolute component will discard all previous component.
* See http://docs.python.org/library/os.path.html#os.path.join
* @param {...string} var_args One of more path components.
* @return {string} The path components joined.
*/
goog.string.path.join = function(var_args) {
var path = arguments[0];
for (var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
if (goog.string.startsWith(arg, '/')) {
path = arg;
} else if (path == '' || goog.string.endsWith(path, '/')) {
path += arg;
} else {
path += '/' + arg;
}
}
return path;
};
/**
* Normalizes a pathname by collapsing duplicate separators, parent directory
* references ('..'), and current directory references ('.').
* See http://docs.python.org/library/os.path.html#os.path.normpath
* @param {string} path One or more path components.
* @return {string} The path after normalization.
*/
goog.string.path.normalizePath = function(path) {
if (path == '') {
return '.';
}
var initialSlashes = '';
// POSIX will keep two slashes, but three or more will be collapsed to one.
if (goog.string.startsWith(path, '/')) {
initialSlashes = '/';
if (goog.string.startsWith(path, '//') &&
!goog.string.startsWith(path, '///')) {
initialSlashes = '//';
}
}
var parts = path.split('/');
var newParts = [];
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
// '' and '.' don't change the directory, ignore.
if (part == '' || part == '.') {
continue;
}
// A '..' should pop a directory unless this is not an absolute path and
// we're at the root, or we've travelled upwards relatively in the last
// iteration.
if (part != '..' ||
(!initialSlashes && !newParts.length) ||
goog.array.peek(newParts) == '..') {
newParts.push(part);
} else {
newParts.pop();
}
}
var returnPath = initialSlashes + newParts.join('/');
return returnPath || '.';
};
/**
* Splits a pathname into "dirname" and "baseName" components, where "baseName"
* is everything after the final slash. Either part may return an empty string.
* See http://docs.python.org/library/os.path.html#os.path.split
* @param {string} path A pathname.
* @return {!Array<string>} An array of [dirname, basename].
*/
goog.string.path.split = function(path) {
var head = goog.string.path.dirname(path);
var tail = goog.string.path.baseName(path);
return [head, tail];
};
// TODO(nnaze): Implement other useful functions from os.path
@@ -0,0 +1,21 @@
<!doctype html>
<html>
<!--
Copyright 2010 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<title>
Closure Unit Tests - goog.string.path
</title>
<script src="../base.js">
</script>
<script>
goog.require('goog.string.pathTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,108 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.pathTest');
goog.setTestOnly('goog.string.pathTest');
goog.require('goog.string.path');
goog.require('goog.testing.jsunit');
// Some test data comes from Python's posixpath tests.
// See http://svn.python.org/view/python/trunk/Lib/test/test_posixpath.py
function testBasename() {
assertEquals('bar', goog.string.path.baseName('/foo/bar'));
assertEquals('', goog.string.path.baseName('/'));
assertEquals('foo', goog.string.path.baseName('foo'));
assertEquals('foo', goog.string.path.baseName('////foo'));
assertEquals('bar', goog.string.path.baseName('//foo//bar'));
}
function testDirname() {
assertEquals('/foo', goog.string.path.dirname('/foo/bar'));
assertEquals('/', goog.string.path.dirname('/'));
assertEquals('', goog.string.path.dirname('foo'));
assertEquals('////', goog.string.path.dirname('////foo'));
assertEquals('//foo', goog.string.path.dirname('//foo//bar'));
}
function testJoin() {
assertEquals('/bar/baz',
goog.string.path.join('/foo', 'bar', '/bar', 'baz'));
assertEquals('/foo/bar/baz',
goog.string.path.join('/foo', 'bar', 'baz'));
assertEquals('/foo/bar/baz',
goog.string.path.join('/foo/', 'bar', 'baz'));
assertEquals('/foo/bar/baz/',
goog.string.path.join('/foo/', 'bar/', 'baz/'));
}
function testNormalizePath() {
assertEquals('.', goog.string.path.normalizePath(''));
assertEquals('.', goog.string.path.normalizePath('./'));
assertEquals('/', goog.string.path.normalizePath('/'));
assertEquals('//', goog.string.path.normalizePath('//'));
assertEquals('/', goog.string.path.normalizePath('///'));
assertEquals('/foo/bar',
goog.string.path.normalizePath('///foo/.//bar//'));
assertEquals('/foo/baz',
goog.string.path.normalizePath('///foo/.//bar//.//..//.//baz'));
assertEquals('/foo/bar',
goog.string.path.normalizePath('///..//./foo/.//bar'));
assertEquals('../../cat/dog',
goog.string.path.normalizePath('../../cat/dog/'));
assertEquals('../dog',
goog.string.path.normalizePath('../cat/../dog/'));
assertEquals('/cat/dog',
goog.string.path.normalizePath('/../cat/dog/'));
assertEquals('/dog',
goog.string.path.normalizePath('/../cat/../dog'));
assertEquals('/dog',
goog.string.path.normalizePath('/../../../dog'));
}
function testSplit() {
assertArrayEquals(['/foo', 'bar'], goog.string.path.split('/foo/bar'));
assertArrayEquals(['/', ''], goog.string.path.split('/'));
assertArrayEquals(['', 'foo'], goog.string.path.split('foo'));
assertArrayEquals(['////', 'foo'], goog.string.path.split('////foo'));
assertArrayEquals(['//foo', 'bar'], goog.string.path.split('//foo//bar'));
}
function testExtension() {
assertEquals('jpg', goog.string.path.extension('././foo/bar/baz.jpg'));
assertEquals('jpg', goog.string.path.extension('././foo bar/baz.jpg'));
assertEquals('jpg', goog.string.path.extension(
'foo/bar/baz/blah blah.jpg'));
assertEquals('', goog.string.path.extension('../../foo/bar/baz baz'));
assertEquals('', goog.string.path.extension('../../foo bar/baz baz'));
assertEquals('', goog.string.path.extension('foo/bar/.'));
assertEquals('', goog.string.path.extension(' '));
assertEquals('', goog.string.path.extension(''));
assertEquals('', goog.string.path.extension('/home/username/.bashrc'));
// Tests cases taken from python os.path.splitext().
assertEquals('bar', goog.string.path.extension('foo.bar'));
assertEquals('bar', goog.string.path.extension('foo.boo.bar'));
assertEquals('bar', goog.string.path.extension('foo.boo.biff.bar'));
assertEquals('rc', goog.string.path.extension('.csh.rc'));
assertEquals('', goog.string.path.extension('nodots'));
assertEquals('', goog.string.path.extension('.cshrc'));
assertEquals('', goog.string.path.extension('...manydots'));
assertEquals('ext', goog.string.path.extension('...manydots.ext'));
assertEquals('', goog.string.path.extension('.'));
assertEquals('', goog.string.path.extension('..'));
assertEquals('', goog.string.path.extension('........'));
assertEquals('', goog.string.path.extension(''));
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2006 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.string</title>
<script src="../base.js"></script>
<script>
goog.require('goog.stringTest');
</script>
</head>
<body>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,103 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility for fast string concatenation.
*/
goog.provide('goog.string.StringBuffer');
/**
* Utility class to facilitate string concatenation.
*
* @param {*=} opt_a1 Optional first initial item to append.
* @param {...*} var_args Other initial items to
* append, e.g., new goog.string.StringBuffer('foo', 'bar').
* @constructor
*/
goog.string.StringBuffer = function(opt_a1, var_args) {
if (opt_a1 != null) {
this.append.apply(this, arguments);
}
};
/**
* Internal buffer for the string to be concatenated.
* @type {string}
* @private
*/
goog.string.StringBuffer.prototype.buffer_ = '';
/**
* Sets the contents of the string buffer object, replacing what's currently
* there.
*
* @param {*} s String to set.
*/
goog.string.StringBuffer.prototype.set = function(s) {
this.buffer_ = '' + s;
};
/**
* Appends one or more items to the buffer.
*
* Calling this with null, undefined, or empty arguments is an error.
*
* @param {*} a1 Required first string.
* @param {*=} opt_a2 Optional second string.
* @param {...*} var_args Other items to append,
* e.g., sb.append('foo', 'bar', 'baz').
* @return {!goog.string.StringBuffer} This same StringBuffer object.
* @suppress {duplicate}
*/
goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
// Use a1 directly to avoid arguments instantiation for single-arg case.
this.buffer_ += a1;
if (opt_a2 != null) { // second argument is undefined (null == undefined)
for (var i = 1; i < arguments.length; i++) {
this.buffer_ += arguments[i];
}
}
return this;
};
/**
* Clears the internal buffer.
*/
goog.string.StringBuffer.prototype.clear = function() {
this.buffer_ = '';
};
/**
* @return {number} the length of the current contents of the buffer.
*/
goog.string.StringBuffer.prototype.getLength = function() {
return this.buffer_.length;
};
/**
* @return {string} The concatenated string.
* @override
*/
goog.string.StringBuffer.prototype.toString = function() {
return this.buffer_;
};
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2006 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>
Closure Unit Tests - goog.string.StringBuffer
</title>
<script src="../base.js">
</script>
<script>
goog.require('goog.string.StringBufferTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,97 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.StringBufferTest');
goog.setTestOnly('goog.string.StringBufferTest');
goog.require('goog.string.StringBuffer');
goog.require('goog.testing.jsunit');
function testStringBuffer() {
var sb = new goog.string.StringBuffer();
sb.append('Four score');
sb.append(' ');
sb.append('and seven years ago.');
assertEquals('Test 1', 'Four score and seven years ago.', sb.toString());
sb.clear();
assertEquals('Test 2', '', sb.toString());
sb = new goog.string.StringBuffer('Four score ');
sb.append('and seven years ago.');
assertEquals('Test 3', 'Four score and seven years ago.', sb.toString());
// can pass in non-Strings?
sb = new goog.string.StringBuffer(1);
sb.append(2);
assertEquals('Test 4', '12', sb.toString());
}
function testStringBufferSet() {
var sb = new goog.string.StringBuffer('foo');
sb.set('bar');
assertEquals('Test 1', 'bar', sb.toString());
}
function testStringBufferMultiAppend() {
var sb = new goog.string.StringBuffer('hey', 'foo');
sb.append('bar', 'baz');
assertEquals('Test 1', 'heyfoobarbaz', sb.toString());
sb = new goog.string.StringBuffer();
sb.append(1, 2);
// should not add up to '3'
assertEquals('Test 2', '12', sb.toString());
}
function testStringBufferToString() {
var sb = new goog.string.StringBuffer('foo', 'bar');
assertEquals('Test 1', 'foobar', sb.toString());
}
function testStringBufferWithFalseFirstArgument() {
var sb = new goog.string.StringBuffer(0, 'more');
assertEquals('Test 1', '0more', sb.toString());
sb = new goog.string.StringBuffer(false, 'more');
assertEquals('Test 2', 'falsemore', sb.toString());
sb = new goog.string.StringBuffer('', 'more');
assertEquals('Test 3', 'more', sb.toString());
sb = new goog.string.StringBuffer(null, 'more');
assertEquals('Test 4', '', sb.toString());
sb = new goog.string.StringBuffer(undefined, 'more');
assertEquals('Test 5', '', sb.toString());
}
function testStringBufferGetLength() {
var sb = new goog.string.StringBuffer();
assertEquals(0, sb.getLength());
sb.append('foo');
assertEquals(3, sb.getLength());
sb.append('baroo');
assertEquals(8, sb.getLength());
sb.clear();
assertEquals(0, sb.getLength());
}
@@ -0,0 +1,251 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Implementation of sprintf-like, python-%-operator-like,
* .NET-String.Format-like functionality. Uses JS string's replace method to
* extract format specifiers and sends those specifiers to a handler function,
* which then, based on conversion type part of the specifier, calls the
* appropriate function to handle the specific conversion.
* For specific functionality implemented, look at formatRe below, or look
* at the tests.
*/
goog.provide('goog.string.format');
goog.require('goog.string');
/**
* Performs sprintf-like conversion, ie. puts the values in a template.
* DO NOT use it instead of built-in conversions in simple cases such as
* 'Cost: %.2f' as it would introduce unneccessary latency oposed to
* 'Cost: ' + cost.toFixed(2).
* @param {string} formatString Template string containing % specifiers.
* @param {...string|number} var_args Values formatString is to be filled with.
* @return {string} Formatted string.
*/
goog.string.format = function(formatString, var_args) {
// Convert the arguments to an array (MDC recommended way).
var args = Array.prototype.slice.call(arguments);
// Try to get the template.
var template = args.shift();
if (typeof template == 'undefined') {
throw Error('[goog.string.format] Template required');
}
// This re is used for matching, it also defines what is supported.
var formatRe = /%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g;
/**
* Chooses which conversion function to call based on type conversion
* specifier.
* @param {string} match Contains the re matched string.
* @param {string} flags Formatting flags.
* @param {string} width Replacement string minimum width.
* @param {string} dotp Matched precision including a dot.
* @param {string} precision Specifies floating point precision.
* @param {string} type Type conversion specifier.
* @param {string} offset Matching location in the original string.
* @param {string} wholeString Has the actualString being searched.
* @return {string} Formatted parameter.
*/
function replacerDemuxer(match,
flags,
width,
dotp,
precision,
type,
offset,
wholeString) {
// The % is too simple and doesn't take an argument.
if (type == '%') {
return '%';
}
// Try to get the actual value from parent function.
var value = args.shift();
// If we didn't get any arguments, fail.
if (typeof value == 'undefined') {
throw Error('[goog.string.format] Not enough arguments');
}
// Patch the value argument to the beginning of our type specific call.
arguments[0] = value;
return goog.string.format.demuxes_[type].apply(null, arguments);
}
return template.replace(formatRe, replacerDemuxer);
};
/**
* Contains various conversion functions (to be filled in later on).
* @type {Object}
* @private
*/
goog.string.format.demuxes_ = {};
/**
* Processes %s conversion specifier.
* @param {string} value Contains the formatRe matched string.
* @param {string} flags Formatting flags.
* @param {string} width Replacement string minimum width.
* @param {string} dotp Matched precision including a dot.
* @param {string} precision Specifies floating point precision.
* @param {string} type Type conversion specifier.
* @param {string} offset Matching location in the original string.
* @param {string} wholeString Has the actualString being searched.
* @return {string} Replacement string.
*/
goog.string.format.demuxes_['s'] = function(value,
flags,
width,
dotp,
precision,
type,
offset,
wholeString) {
var replacement = value;
// If no padding is necessary we're done.
// The check for '' is necessary because Firefox incorrectly provides the
// empty string instead of undefined for non-participating capture groups,
// and isNaN('') == false.
if (isNaN(width) || width == '' || replacement.length >= width) {
return replacement;
}
// Otherwise we should find out where to put spaces.
if (flags.indexOf('-', 0) > -1) {
replacement =
replacement + goog.string.repeat(' ', width - replacement.length);
} else {
replacement =
goog.string.repeat(' ', width - replacement.length) + replacement;
}
return replacement;
};
/**
* Processes %f conversion specifier.
* @param {number} value Contains the formatRe matched string.
* @param {string} flags Formatting flags.
* @param {string} width Replacement string minimum width.
* @param {string} dotp Matched precision including a dot.
* @param {string} precision Specifies floating point precision.
* @param {string} type Type conversion specifier.
* @param {string} offset Matching location in the original string.
* @param {string} wholeString Has the actualString being searched.
* @return {string} Replacement string.
*/
goog.string.format.demuxes_['f'] = function(value,
flags,
width,
dotp,
precision,
type,
offset,
wholeString) {
var replacement = value.toString();
// The check for '' is necessary because Firefox incorrectly provides the
// empty string instead of undefined for non-participating capture groups,
// and isNaN('') == false.
if (!(isNaN(precision) || precision == '')) {
replacement = value.toFixed(precision);
}
// Generates sign string that will be attached to the replacement.
var sign;
if (value < 0) {
sign = '-';
} else if (flags.indexOf('+') >= 0) {
sign = '+';
} else if (flags.indexOf(' ') >= 0) {
sign = ' ';
} else {
sign = '';
}
if (value >= 0) {
replacement = sign + replacement;
}
// If no padding is neccessary we're done.
if (isNaN(width) || replacement.length >= width) {
return replacement;
}
// We need a clean signless replacement to start with
replacement = isNaN(precision) ?
Math.abs(value).toString() :
Math.abs(value).toFixed(precision);
var padCount = width - replacement.length - sign.length;
// Find out which side to pad, and if it's left side, then which character to
// pad, and set the sign on the left and padding in the middle.
if (flags.indexOf('-', 0) >= 0) {
replacement = sign + replacement + goog.string.repeat(' ', padCount);
} else {
// Decides which character to pad.
var paddingChar = (flags.indexOf('0', 0) >= 0) ? '0' : ' ';
replacement =
sign + goog.string.repeat(paddingChar, padCount) + replacement;
}
return replacement;
};
/**
* Processes %d conversion specifier.
* @param {string} value Contains the formatRe matched string.
* @param {string} flags Formatting flags.
* @param {string} width Replacement string minimum width.
* @param {string} dotp Matched precision including a dot.
* @param {string} precision Specifies floating point precision.
* @param {string} type Type conversion specifier.
* @param {string} offset Matching location in the original string.
* @param {string} wholeString Has the actualString being searched.
* @return {string} Replacement string.
*/
goog.string.format.demuxes_['d'] = function(value,
flags,
width,
dotp,
precision,
type,
offset,
wholeString) {
return goog.string.format.demuxes_['f'](
parseInt(value, 10) /* value */,
flags, width, dotp, 0 /* precision */,
type, offset, wholeString);
};
// These are additional aliases, for integer conversion.
goog.string.format.demuxes_['i'] = goog.string.format.demuxes_['d'];
goog.string.format.demuxes_['u'] = goog.string.format.demuxes_['d'];
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.
Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>
Closure Unit Tests - goog.string.format
</title>
<script src="../base.js">
</script>
<script>
goog.require('goog.string.formatTest');
</script>
</head>
<body>
</body>
</html>
@@ -0,0 +1,218 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.formatTest');
goog.setTestOnly('goog.string.formatTest');
goog.require('goog.string.format');
goog.require('goog.testing.jsunit');
// The discussion on naming this functionality is going on.
var f = goog.string.format;
function testImmediateFormatSpecifier() {
assertEquals('Empty String', '', f(''));
assertEquals('Immediate Value', 'Immediate Value', f('Immediate Value'));
}
function testPercentSign() {
assertEquals('%', '%', f('%'));
assertEquals('%%', '%', f('%%'));
assertEquals('%%%', '%%', f('%%%'));
assertEquals('%%%%', '%%', f('%%%%'));
assertEquals('width of the percent sign ???', '%%', f('%345%%-67.987%'));
}
function testStringConversionSpecifier() {
assertEquals('%s', 'abc', f('%s', 'abc'));
assertEquals('%2s', 'abc', f('%2s', 'abc'));
assertEquals('%6s', ' abc', f('%6s', 'abc'));
assertEquals('%-6s', 'abc ', f('%-6s', 'abc'));
}
function testFloatConversionSpecifier() {
assertEquals('%f', '123', f('%f', 123));
assertEquals('%f', '0.1', f('%f', 0.1));
assertEquals('%f', '123.456', f('%f', 123.456));
// Precisions, paddings and other flags are handled on a flag to flag basis.
}
function testAliasedConversionSpecifiers() {
assertEquals('%i vs. %d', f('%i', 123), f('%d', 123));
assertEquals('%u vs. %d', f('%u', 123), f('%d', 123));
}
function testIntegerConversion() {
assertEquals('%d', '0', f('%d', 0));
assertEquals('%d', '123', f('%d', 123));
assertEquals('%d', '0', f('%d', 0.1));
assertEquals('%d', '0', f('%d', 0.9));
assertEquals('%d', '123', f('%d', 123.456));
assertEquals('%d', '-1', f('%d', -1));
assertEquals('%d', '0', f('%d', -0.1));
assertEquals('%d', '0', f('%d', -0.9));
assertEquals('%d', '-123', f('%d', -123.456));
// Precisions, paddings and other flags are handled on a flag to flag basis.
}
function testSpaceFlag() {
assertEquals('zero %+d ', ' 0', f('% d', 0));
assertEquals('positive % d ', ' 123', f('% d', 123));
assertEquals('negative % d ', '-123', f('% d', -123));
assertEquals('positive % 3d', ' 123', f('% 3d', 123));
assertEquals('negative % 3d', '-123', f('% 3d', -123));
assertEquals('positive % 4d', ' 123', f('% 4d', 123));
assertEquals('negative % 4d', '-123', f('% 4d', -123));
assertEquals('positive % 6d', ' 123', f('% 6d', 123));
assertEquals('negative % 6d', '- 123', f('% 6d', -123));
assertEquals('positive % f ', ' 123.456', f('% f', 123.456));
assertEquals('negative % f ', '-123.456', f('% f', -123.456));
assertEquals('positive % .2f ', ' 123.46', f('% .2f', 123.456));
assertEquals('negative % .2f ', '-123.46', f('% .2f', -123.456));
assertEquals('positive % 6.2f', ' 123.46', f('% 6.2f', 123.456));
assertEquals('negative % 6.2f', '-123.46', f('% 6.2f', -123.456));
assertEquals('positive % 7.2f', ' 123.46', f('% 7.2f', 123.456));
assertEquals('negative % 7.2f', '-123.46', f('% 7.2f', -123.456));
assertEquals('positive % 10.2f', ' 123.46', f('% 10.2f', 123.456));
assertEquals('negative % 10.2f', '- 123.46', f('% 10.2f', -123.456));
assertEquals('string % s ', 'abc', f('% s', 'abc'));
assertEquals('string % 3s', 'abc', f('% 3s', 'abc'));
assertEquals('string % 4s', ' abc', f('% 4s', 'abc'));
assertEquals('string % 6s', ' abc', f('% 6s', 'abc'));
}
function testPlusFlag() {
assertEquals('zero %+d ', '+0', f('%+d', 0));
assertEquals('positive %+d ', '+123', f('%+d', 123));
assertEquals('negative %+d ', '-123', f('%+d', -123));
assertEquals('positive %+3d', '+123', f('%+3d', 123));
assertEquals('negative %+3d', '-123', f('%+3d', -123));
assertEquals('positive %+4d', '+123', f('%+4d', 123));
assertEquals('negative %+4d', '-123', f('%+4d', -123));
assertEquals('positive %+6d', '+ 123', f('%+6d', 123));
assertEquals('negative %+6d', '- 123', f('%+6d', -123));
assertEquals('positive %+f ', '+123.456', f('%+f', 123.456));
assertEquals('negative %+f ', '-123.456', f('%+f', -123.456));
assertEquals('positive %+.2f ', '+123.46', f('%+.2f', 123.456));
assertEquals('negative %+.2f ', '-123.46', f('%+.2f', -123.456));
assertEquals('positive %+6.2f', '+123.46', f('%+6.2f', 123.456));
assertEquals('negative %+6.2f', '-123.46', f('%+6.2f', -123.456));
assertEquals('positive %+7.2f', '+123.46', f('%+7.2f', 123.456));
assertEquals('negative %+7.2f', '-123.46', f('%+7.2f', -123.456));
assertEquals('positive %+10.2f', '+ 123.46', f('%+10.2f', 123.456));
assertEquals('negative %+10.2f', '- 123.46', f('%+10.2f', -123.456));
assertEquals('string %+s ', 'abc', f('%+s', 'abc'));
assertEquals('string %+3s', 'abc', f('%+3s', 'abc'));
assertEquals('string %+4s', ' abc', f('%+4s', 'abc'));
assertEquals('string %+6s', ' abc', f('%+6s', 'abc'));
}
function testPrecision() {
assertEquals('%.5d', '0', f('%.5d', 0));
assertEquals('%d', '123', f('%d', 123.456));
assertEquals('%.2d', '123', f('%.2d', 123.456));
assertEquals('%f', '123.456', f('%f', 123.456));
assertEquals('%.2f', '123.46', f('%.2f', 123.456));
assertEquals('%.3f', '123.456', f('%.3f', 123.456));
assertEquals('%.6f', '123.456000', f('%.6f', 123.456));
assertEquals('%1.2f', '123.46', f('%1.2f', 123.456));
assertEquals('%7.2f', ' 123.46', f('%7.2f', 123.456));
assertEquals('%5.6f', '123.456000', f('%5.6f', 123.456));
assertEquals('%11.6f', ' 123.456000', f('%11.6f', 123.456));
assertEquals('%07.2f', '0123.46', f('%07.2f', 123.456));
assertEquals('%+7.2f', '+123.46', f('%+7.2f', 123.456));
}
function testZeroFlag() {
assertEquals('%0s', 'abc', f('%0s', 'abc'));
assertEquals('%02s', 'abc', f('%02s', 'abc'));
assertEquals('%06s', ' abc', f('%06s', 'abc'));
assertEquals('%0d', '123', f('%0d', 123));
assertEquals('%0d', '-123', f('%0d', -123));
assertEquals('%06d', '000123', f('%06d', 123));
assertEquals('%06d', '-00123', f('%06d', -123));
assertEquals('%010d', '0000000123', f('%010d', 123));
assertEquals('%010d', '-000000123', f('%010d', -123));
}
function testFlagMinus() {
assertEquals('%-s', 'abc', f('%-s', 'abc'));
assertEquals('%-2s', 'abc', f('%-2s', 'abc'));
assertEquals('%-6s', 'abc ', f('%-6s', 'abc'));
assertEquals('%-d', '123', f('%-d', 123));
assertEquals('%-d', '-123', f('%-d', -123));
assertEquals('%-2d', '123', f('%-2d', 123));
assertEquals('%-2d', '-123', f('%-2d', -123));
assertEquals('%-4d', '123 ', f('%-4d', 123));
assertEquals('%-4d', '-123', f('%-4d', -123));
assertEquals('%-d', '123', f('%-0d', 123));
assertEquals('%-d', '-123', f('%-0d', -123));
assertEquals('%-4d', '123 ', f('%-04d', 123));
assertEquals('%-4d', '-123', f('%-04d', -123));
}
function testExceptions() {
var e = assertThrows(goog.partial(f, '%f%f', 123.456));
assertEquals('[goog.string.format] Not enough arguments', e.message);
e = assertThrows(f);
assertEquals('[goog.string.format] Template required', e.message);
}
function testNonParticipatingGroupHandling() {
// Firefox supplies empty string instead of undefined for non-participating
// capture groups. This can trigger bad behavior if a demuxer only checks
// isNaN(val) and not also val == ''. Check for regressions.
var format = '%s %d %i %u';
var expected = '1 2 3 4';
// Good types
assertEquals(expected, goog.string.format(format, 1, '2', '3', '4'));
// Bad types
assertEquals(expected, goog.string.format(format, '1', 2, 3, 4));
}
@@ -0,0 +1,38 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines an interface for serializing objects into strings.
*/
goog.provide('goog.string.Stringifier');
/**
* An interface for serializing objects into strings.
* @interface
*/
goog.string.Stringifier = function() {};
/**
* Serializes an object or a value to a string.
* Agnostic to the particular format of object and string.
*
* @param {*} object The object to stringify.
* @return {string} A string representation of the input.
*/
goog.string.Stringifier.prototype.stringify;
@@ -0,0 +1,48 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.TypedString');
/**
* Wrapper for strings that conform to a data type or language.
*
* Implementations of this interface are wrappers for strings, and typically
* associate a type contract with the wrapped string. Concrete implementations
* of this interface may choose to implement additional run-time type checking,
* see for example {@code goog.html.SafeHtml}. If available, client code that
* needs to ensure type membership of an object should use the type's function
* to assert type membership, such as {@code goog.html.SafeHtml.unwrap}.
* @interface
*/
goog.string.TypedString = function() {};
/**
* Interface marker of the TypedString interface.
*
* This property can be used to determine at runtime whether or not an object
* implements this interface. All implementations of this interface set this
* property to {@code true}.
* @type {boolean}
*/
goog.string.TypedString.prototype.implementsGoogStringTypedString;
/**
* Retrieves this wrapped string's value.
* @return {!string} The wrapped string's value.
*/
goog.string.TypedString.prototype.getTypedStringValue;