diff --git a/.gitignore b/.gitignore
index 3142dc3b70ea..47bab2dd26a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-build/
+# Refer http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
+
+/build/
angularjs.netrc
jstd.log
.DS_Store
@@ -12,3 +14,4 @@ angular.js.tmproj
node_modules
angular.xcodeproj
.idea
+*.pyc
diff --git a/Rakefile b/Rakefile
index 069579e5e061..88355b14f6ac 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,3 +1,4 @@
+require 'tempfile'
require 'yaml'
include FileUtils
@@ -60,9 +61,10 @@ end
desc 'Concat AngularJS files'
task :concat => :init do
+ angularjs_temp = closure_builder(files['angularSrc'])
concat_file('angular.js', [
'src/angular.prefix',
- files['angularSrc'],
+ angularjs_temp,
'src/angular.suffix',
], gen_css('css/angular.css', true))
@@ -306,6 +308,21 @@ def closure_compile(filename)
end
+def closure_builder(deps)
+ tempfile = Tempfile.new('angular-').path
+ puts "Invoking closure_builder.py --output_file=#{tempfile} ..."
+ sh "./lib/closure-library/closure/bin/build/closurebuilder.py" +
+ " --root=./lib/closure-library/closure" +
+ " --namespace=angular" +
+ " --output_mode=script" +
+ " --output_file=#{tempfile}" +
+ " " + deps.flatten.join(" ")
+ rewrite_file(tempfile) do |content| fixup_concat_contents(content)
+ return tempfile
+ end
+end
+
+
def concat_file(filename, deps, footer='')
puts "Creating #{filename} ..."
File.open(path_to(filename), 'w') do |f|
@@ -316,9 +333,8 @@ def concat_file(filename, deps, footer='')
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
- gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
- gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
- sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
+ gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename)
+ content = fixup_concat_contents(content)
f.write(content)
f.write(footer)
@@ -331,13 +347,22 @@ def concat_module(name, files, footer='')
end
+def fixup_concat_contents(content)
+ return content.
+ gsub(/^\s*goog\.(require|provide)\(.*$/, ''). # remove all goog.require / goog.provide directives
+ gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
+ sub(/\n\/\/ BEGIN_FILE: goog\/base.js\n.*\n\/\/ END_FILE: goog\/base.js/m, "") # remove goog\/base.js
+ sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
+end
+
+
def rewrite_file(filename)
File.open(filename, File::RDWR) do |f|
content = f.read
content = yield content
- raise "File rewrite failed - No content!" unless content
+ raise "File rewrite failed - No content! (file: #{filename}" unless content
f.truncate 0
f.rewind
diff --git a/angularFiles.js b/angularFiles.js
index 47f18961f736..be070f609d14 100644
--- a/angularFiles.js
+++ b/angularFiles.js
@@ -8,6 +8,8 @@ angularFiles = {
'src/auto/injector.js',
+ 'src/_all.js',
+ 'src/ng/_all.js',
'src/ng/anchorScroll.js',
'src/ng/browser.js',
'src/ng/cacheFactory.js',
@@ -31,11 +33,13 @@ angularFiles = {
'src/ng/timeout.js',
'src/ng/filter.js',
+ 'src/ng/filter/_all.js',
'src/ng/filter/filter.js',
'src/ng/filter/filters.js',
'src/ng/filter/limitTo.js',
'src/ng/filter/orderBy.js',
+ 'src/ng/directive/_all.js',
'src/ng/directive/directives.js',
'src/ng/directive/a.js',
'src/ng/directive/booleanAttrs.js',
@@ -63,8 +67,11 @@ angularFiles = {
],
'angularSrcModules': [
+ 'src/ngCookies/_all.js',
'src/ngCookies/cookies.js',
+ 'src/ngResource/_all.js',
'src/ngResource/resource.js',
+ 'src/ngSanitize/_all.js',
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js',
@@ -74,6 +81,7 @@ angularFiles = {
],
'angularScenario': [
+ 'src/ngScenario/_all.js',
'src/ngScenario/Scenario.js',
'src/ngScenario/Application.js',
'src/ngScenario/Describe.js',
@@ -114,6 +122,7 @@ angularFiles = {
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'lib/jquery/jquery.js',
'test/jquery_remove.js',
+ 'lib/closure-library/closure/goog/base.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
diff --git a/lib/closure-compiler/README b/lib/closure-compiler/README
index ece717586fff..270cfc278ca5 100644
--- a/lib/closure-compiler/README
+++ b/lib/closure-compiler/README
@@ -146,13 +146,13 @@ the parse tree data structures were extracted and modified
significantly for use by Google's JavaScript compiler.
Local Modifications: The packages have been renamespaced. All code not
-relavant to parsing has been removed. A JSDoc parser and static typing
+relevant to parsing has been removed. A JsDoc parser and static typing
system have been added.
-----
Code in:
-lib/libtrunk_rhino_parser_jarjared.jar
+lib/rhino
Rhino
URL: http://www.mozilla.org/rhino
@@ -161,9 +161,8 @@ License: Netscape Public License and MPL / GPL dual license
Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
-Local Modifications: None. We've used JarJar to renamespace the code
-post-compilation. See:
-http://code.google.com/p/jarjar/
+Local Modifications: Minor changes to parsing JSDoc that usually get pushed
+up-stream to Rhino trunk.
-----
@@ -172,7 +171,7 @@ lib/args4j.jar
Args4j
URL: https://args4j.dev.java.net/
-Version: 2.0.12
+Version: 2.0.16
License: MIT
Description:
@@ -188,7 +187,7 @@ lib/guava.jar
Guava Libraries
URL: http://code.google.com/p/guava-libraries/
-Version: r08
+Version: 13.0.1
License: Apache License 2.0
Description: Google's core Java libraries.
@@ -210,13 +209,28 @@ Description: Annotations for software defect detection.
Local Modifications: None.
+-----
+Code in:
+lib/jarjar.jar
+
+Jar Jar Links
+URL: http://jarjar.googlecode.com/
+Version: 1.1
+License: Apache License 2.0
+
+Description:
+A utility for repackaging Java libraries.
+
+Local Modifications: None.
+
+
----
Code in:
lib/junit.jar
JUnit
URL: http://sourceforge.net/projects/junit/
-Version: 4.8.2
+Version: 4.10
License: Common Public License 1.0
Description: A framework for writing and running automated tests in Java.
@@ -230,7 +244,7 @@ lib/protobuf-java.jar
Protocol Buffers
URL: http://code.google.com/p/protobuf/
-Version: 2.3.0
+Version: 2.4.1
License: New BSD License
Description: Supporting libraries for protocol buffers,
@@ -267,9 +281,9 @@ Local Modifications: None
---
Code in:
-tools/maven-ant-tasks-2.1.1.jar
+tools/maven-ant-tasks-2.1.3.jar
URL: http://maven.apache.org
-Version 2.1.1
+Version 2.1.3
License: Apache License 2.0
Description:
Maven Ant tasks are used to manage dependencies and to install/deploy to
diff --git a/lib/closure-compiler/compiler.jar b/lib/closure-compiler/compiler.jar
index 694808c0cac6..53037b0e8a8d 100644
Binary files a/lib/closure-compiler/compiler.jar and b/lib/closure-compiler/compiler.jar differ
diff --git a/lib/closure-compiler/version.txt b/lib/closure-compiler/version.txt
index df60031294fa..0e1ecf6bce8d 100644
--- a/lib/closure-compiler/version.txt
+++ b/lib/closure-compiler/version.txt
@@ -1 +1,3 @@
-20110615
+Version: 20121212 built at r2388
+
+Url: http://closure-compiler.googlecode.com/files/compiler-20121212.zip
diff --git a/lib/closure-library/closure/bin/build/closurebuilder.py b/lib/closure-library/closure/bin/build/closurebuilder.py
new file mode 100755
index 000000000000..3f3b946f4d98
--- /dev/null
+++ b/lib/closure-library/closure/bin/build/closurebuilder.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 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.
+
+"""Utility for Closure Library dependency calculation.
+
+ClosureBuilder scans source files to build dependency info. From the
+dependencies, the script can produce a manifest in dependency order,
+a concatenated script, or compiled output from the Closure Compiler.
+
+Paths to files can be expressed as individual arguments to the tool (intended
+for use with find and xargs). As a convenience, --root can be used to specify
+all JS files below a directory.
+
+usage: %prog [options] [file1.js file2.js ...]
+"""
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+import logging
+import optparse
+import os
+import sys
+
+import depstree
+import jscompiler
+import source
+import treescan
+
+
+def _GetOptionsParser():
+ """Get the options parser."""
+
+ parser = optparse.OptionParser(__doc__)
+ parser.add_option('-i',
+ '--input',
+ dest='inputs',
+ action='append',
+ default=[],
+ help='One or more input files to calculate dependencies '
+ 'for. The namespaces in this file will be combined with '
+ 'those given with the -n flag to form the set of '
+ 'namespaces to find dependencies for.')
+ parser.add_option('-n',
+ '--namespace',
+ dest='namespaces',
+ action='append',
+ default=[],
+ help='One or more namespaces to calculate dependencies '
+ 'for. These namespaces will be combined with those given '
+ 'with the -i flag to form the set of namespaces to find '
+ 'dependencies for. A Closure namespace is a '
+ 'dot-delimited path expression declared with a call to '
+ 'goog.provide() (e.g. "goog.array" or "foo.bar").')
+ parser.add_option('--root',
+ dest='roots',
+ action='append',
+ default=[],
+ help='The paths that should be traversed to build the '
+ 'dependencies.')
+ parser.add_option('-o',
+ '--output_mode',
+ dest='output_mode',
+ type='choice',
+ action='store',
+ choices=['list', 'script', 'compiled'],
+ default='list',
+ help='The type of output to generate from this script. '
+ 'Options are "list" for a list of filenames, "script" '
+ 'for a single script containing the contents of all the '
+ 'files, or "compiled" to produce compiled output with '
+ 'the Closure Compiler. Default is "list".')
+ parser.add_option('-c',
+ '--compiler_jar',
+ dest='compiler_jar',
+ action='store',
+ help='The location of the Closure compiler .jar file.')
+ parser.add_option('-f',
+ '--compiler_flags',
+ dest='compiler_flags',
+ default=[],
+ action='append',
+ help='Additional flags to pass to the Closure compiler. '
+ 'To pass multiple flags, --compiler_flags has to be '
+ 'specified multiple times.')
+ parser.add_option('--output_file',
+ dest='output_file',
+ action='store',
+ help=('If specified, write output to this path instead of '
+ 'writing to standard output.'))
+
+ return parser
+
+
+def _GetInputByPath(path, sources):
+ """Get the source identified by a path.
+
+ Args:
+ path: str, A path to a file that identifies a source.
+ sources: An iterable collection of source objects.
+
+ Returns:
+ The source from sources identified by path, if found. Converts to
+ absolute paths for comparison.
+ """
+ for js_source in sources:
+ # Convert both to absolute paths for comparison.
+ if os.path.abspath(path) == os.path.abspath(js_source.GetPath()):
+ return js_source
+
+
+def _GetClosureBaseFile(sources):
+ """Given a set of sources, returns the one base.js file.
+
+ Note that if zero or two or more base.js files are found, an error message
+ will be written and the program will be exited.
+
+ Args:
+ sources: An iterable of _PathSource objects.
+
+ Returns:
+ The _PathSource representing the base Closure file.
+ """
+ base_files = [
+ js_source for js_source in sources if _IsClosureBaseFile(js_source)]
+
+ if not base_files:
+ logging.error('No Closure base.js file found.')
+ sys.exit(1)
+ if len(base_files) > 1:
+ logging.error('More than one Closure base.js files found at these paths:')
+ for base_file in base_files:
+ logging.error(base_file.GetPath())
+ sys.exit(1)
+ return base_files[0]
+
+
+def _IsClosureBaseFile(js_source):
+ """Returns true if the given _PathSource is the Closure base.js source."""
+ return (os.path.basename(js_source.GetPath()) == 'base.js' and
+ js_source.provides == set(['goog']))
+
+
+class _PathSource(source.Source):
+ """Source file subclass that remembers its file path."""
+
+ def __init__(self, path):
+ """Initialize a source.
+
+ Args:
+ path: str, Path to a JavaScript file. The source string will be read
+ from this file.
+ """
+ super(_PathSource, self).__init__(source.GetFileContents(path))
+
+ self._path = path
+
+ def GetPath(self):
+ """Returns the path."""
+ return self._path
+
+
+def main():
+ logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
+ level=logging.INFO)
+ options, args = _GetOptionsParser().parse_args()
+
+ # Make our output pipe.
+ if options.output_file:
+ out = open(options.output_file, 'w')
+ else:
+ out = sys.stdout
+
+ sources = set()
+
+ logging.info('Scanning paths...')
+ for path in options.roots:
+ for js_path in treescan.ScanTreeForJsFiles(path):
+ sources.add(_PathSource(js_path))
+
+ # Add scripts specified on the command line.
+ for js_path in args:
+ sources.add(_PathSource(js_path))
+
+ logging.info('%s sources scanned.', len(sources))
+
+ # Though deps output doesn't need to query the tree, we still build it
+ # to validate dependencies.
+ logging.info('Building dependency tree..')
+ tree = depstree.DepsTree(sources)
+
+ input_namespaces = set()
+ inputs = options.inputs or []
+ for input_path in inputs:
+ js_input = _GetInputByPath(input_path, sources)
+ if not js_input:
+ logging.error('No source matched input %s', input_path)
+ sys.exit(1)
+ input_namespaces.update(js_input.provides)
+
+ input_namespaces.update(options.namespaces)
+
+ if not input_namespaces:
+ logging.error('No namespaces found. At least one namespace must be '
+ 'specified with the --namespace or --input flags.')
+ sys.exit(2)
+
+ # The Closure Library base file must go first.
+ base = _GetClosureBaseFile(sources)
+ deps = [base] + tree.GetDependencies(input_namespaces)
+
+ output_mode = options.output_mode
+ if output_mode == 'list':
+ out.writelines([js_source.GetPath() + '\n' for js_source in deps])
+ elif output_mode == 'script':
+ out.writelines([js_source.GetSource() for js_source in deps])
+ elif output_mode == 'compiled':
+
+ # Make sure a .jar is specified.
+ if not options.compiler_jar:
+ logging.error('--compiler_jar flag must be specified if --output is '
+ '"compiled"')
+ sys.exit(2)
+
+ compiled_source = jscompiler.Compile(
+ options.compiler_jar,
+ [js_source.GetPath() for js_source in deps],
+ options.compiler_flags)
+
+ if compiled_source is None:
+ logging.error('JavaScript compilation failed.')
+ sys.exit(1)
+ else:
+ logging.info('JavaScript compilation succeeded.')
+ out.write(compiled_source)
+
+ else:
+ logging.error('Invalid value for --output flag.')
+ sys.exit(2)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/closure-library/closure/bin/build/depstree.py b/lib/closure-library/closure/bin/build/depstree.py
new file mode 100755
index 000000000000..f288dd3aa616
--- /dev/null
+++ b/lib/closure-library/closure/bin/build/depstree.py
@@ -0,0 +1,189 @@
+# Copyright 2009 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.
+
+
+"""Class to represent a full Closure Library dependency tree.
+
+Offers a queryable tree of dependencies of a given set of sources. The tree
+will also do logical validation to prevent duplicate provides and circular
+dependencies.
+"""
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+class DepsTree(object):
+ """Represents the set of dependencies between source files."""
+
+ def __init__(self, sources):
+ """Initializes the tree with a set of sources.
+
+ Args:
+ sources: A set of JavaScript sources.
+
+ Raises:
+ MultipleProvideError: A namespace is provided by muplitple sources.
+ NamespaceNotFoundError: A namespace is required but never provided.
+ """
+
+ self._sources = sources
+ self._provides_map = dict()
+
+ # Ensure nothing was provided twice.
+ for source in sources:
+ for provide in source.provides:
+ if provide in self._provides_map:
+ raise MultipleProvideError(
+ provide, [self._provides_map[provide], source])
+
+ self._provides_map[provide] = source
+
+ # Check that all required namespaces are provided.
+ for source in sources:
+ for require in source.requires:
+ if require not in self._provides_map:
+ raise NamespaceNotFoundError(require, source)
+
+ def GetDependencies(self, required_namespaces):
+ """Get source dependencies, in order, for the given namespaces.
+
+ Args:
+ required_namespaces: A string (for one) or list (for one or more) of
+ namespaces.
+
+ Returns:
+ A list of source objects that provide those namespaces and all
+ requirements, in dependency order.
+
+ Raises:
+ NamespaceNotFoundError: A namespace is requested but doesn't exist.
+ CircularDependencyError: A cycle is detected in the dependency tree.
+ """
+ if isinstance(required_namespaces, str):
+ required_namespaces = [required_namespaces]
+
+ deps_sources = []
+
+ for namespace in required_namespaces:
+ for source in DepsTree._ResolveDependencies(
+ namespace, [], self._provides_map, []):
+ if source not in deps_sources:
+ deps_sources.append(source)
+
+ return deps_sources
+
+ @staticmethod
+ def _ResolveDependencies(required_namespace, deps_list, provides_map,
+ traversal_path):
+ """Resolve dependencies for Closure source files.
+
+ Follows the dependency tree down and builds a list of sources in dependency
+ order. This function will recursively call itself to fill all dependencies
+ below the requested namespaces, and then append its sources at the end of
+ the list.
+
+ Args:
+ required_namespace: String of required namespace.
+ deps_list: List of sources in dependency order. This function will append
+ the required source once all of its dependencies are satisfied.
+ provides_map: Map from namespace to source that provides it.
+ traversal_path: List of namespaces of our path from the root down the
+ dependency/recursion tree. Used to identify cyclical dependencies.
+ This is a list used as a stack -- when the function is entered, the
+ current namespace is pushed and popped right before returning.
+ Each recursive call will check that the current namespace does not
+ appear in the list, throwing a CircularDependencyError if it does.
+
+ Returns:
+ The given deps_list object filled with sources in dependency order.
+
+ Raises:
+ NamespaceNotFoundError: A namespace is requested but doesn't exist.
+ CircularDependencyError: A cycle is detected in the dependency tree.
+ """
+
+ source = provides_map.get(required_namespace)
+ if not source:
+ raise NamespaceNotFoundError(required_namespace)
+
+ if required_namespace in traversal_path:
+ traversal_path.append(required_namespace) # do this *after* the test
+
+ # This must be a cycle.
+ raise CircularDependencyError(traversal_path)
+
+ # If we don't have the source yet, we'll have to visit this namespace and
+ # add the required dependencies to deps_list.
+ if source not in deps_list:
+ traversal_path.append(required_namespace)
+
+ for require in source.requires:
+
+ # Append all other dependencies before we append our own.
+ DepsTree._ResolveDependencies(require, deps_list, provides_map,
+ traversal_path)
+ deps_list.append(source)
+
+ traversal_path.pop()
+
+ return deps_list
+
+
+class BaseDepsTreeError(Exception):
+ """Base DepsTree error."""
+
+ def __init__(self):
+ Exception.__init__(self)
+
+
+class CircularDependencyError(BaseDepsTreeError):
+ """Raised when a dependency cycle is encountered."""
+
+ def __init__(self, dependency_list):
+ BaseDepsTreeError.__init__(self)
+ self._dependency_list = dependency_list
+
+ def __str__(self):
+ return ('Encountered circular dependency:\n%s\n' %
+ '\n'.join(self._dependency_list))
+
+
+class MultipleProvideError(BaseDepsTreeError):
+ """Raised when a namespace is provided more than once."""
+
+ def __init__(self, namespace, sources):
+ BaseDepsTreeError.__init__(self)
+ self._namespace = namespace
+ self._sources = sources
+
+ def __str__(self):
+ source_strs = map(str, self._sources)
+
+ return ('Namespace "%s" provided more than once in sources:\n%s\n' %
+ (self._namespace, '\n'.join(source_strs)))
+
+
+class NamespaceNotFoundError(BaseDepsTreeError):
+ """Raised when a namespace is requested but not provided."""
+
+ def __init__(self, namespace, source=None):
+ BaseDepsTreeError.__init__(self)
+ self._namespace = namespace
+ self._source = source
+
+ def __str__(self):
+ msg = 'Namespace "%s" never provided.' % self._namespace
+ if self._source:
+ msg += ' Required in %s' % self._source
+ return msg
diff --git a/lib/closure-library/closure/bin/build/jscompiler.py b/lib/closure-library/closure/bin/build/jscompiler.py
new file mode 100755
index 000000000000..fd1882fdb3b9
--- /dev/null
+++ b/lib/closure-library/closure/bin/build/jscompiler.py
@@ -0,0 +1,71 @@
+# 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.
+
+"""Utility to use the Closure Compiler CLI from Python."""
+
+import distutils.version
+import logging
+import re
+import subprocess
+
+
+# Pulls a version number from the first line of 'java -version'
+# See http://java.sun.com/j2se/versioning_naming.html to learn more about the
+# command's output format.
+_VERSION_REGEX = re.compile('"([0-9][.0-9]*)')
+
+
+def _GetJavaVersion():
+ """Returns the string for the current version of Java installed."""
+ proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE)
+ unused_stdoutdata, stderrdata = proc.communicate()
+ version_line = stderrdata.splitlines()[0]
+ return _VERSION_REGEX.search(version_line).group(1)
+
+
+def Compile(compiler_jar_path, source_paths, flags=None):
+ """Prepares command-line call to Closure Compiler.
+
+ Args:
+ compiler_jar_path: Path to the Closure compiler .jar file.
+ source_paths: Source paths to build, in order.
+ flags: A list of additional flags to pass on to Closure Compiler.
+
+ Returns:
+ The compiled source, as a string, or None if compilation failed.
+ """
+
+ # User friendly version check.
+ if not (distutils.version.LooseVersion(_GetJavaVersion()) >=
+ distutils.version.LooseVersion('1.6')):
+ logging.error('Closure Compiler requires Java 1.6 or higher. '
+ 'Please visit http://www.java.com/getjava')
+ return
+
+ args = ['java', '-jar', compiler_jar_path]
+ for path in source_paths:
+ args += ['--js', path]
+
+ if flags:
+ args += flags
+
+ logging.info('Compiling with the following command: %s', ' '.join(args))
+
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+ stdoutdata, unused_stderrdata = proc.communicate()
+
+ if proc.returncode != 0:
+ return
+
+ return stdoutdata
diff --git a/lib/closure-library/closure/bin/build/source.py b/lib/closure-library/closure/bin/build/source.py
new file mode 100755
index 000000000000..c2ee1fbb26f6
--- /dev/null
+++ b/lib/closure-library/closure/bin/build/source.py
@@ -0,0 +1,114 @@
+# Copyright 2009 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.
+
+
+"""Scans a source JS file for its provided and required namespaces.
+
+Simple class to scan a JavaScript file and express its dependencies.
+"""
+
+__author__ = 'nnaze@google.com'
+
+
+import re
+
+_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)'
+_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide')
+_REQUIRES_REGEX = re.compile(_BASE_REGEX_STRING % 'require')
+
+# This line identifies base.js and should match the line in that file.
+_GOOG_BASE_LINE = (
+ 'var goog = goog || {}; // Identifies this file as the Closure base.')
+
+
+class Source(object):
+ """Scans a JavaScript source for its provided and required namespaces."""
+
+ # Matches a "/* ... */" comment.
+ # Note: We can't definitively distinguish a "/*" in a string literal without a
+ # state machine tokenizer. We'll assume that a line starting with whitespace
+ # and "/*" is a comment.
+ _COMMENT_REGEX = re.compile(
+ r"""
+ ^\s* # Start of a new line and whitespace
+ /\* # Opening "/*"
+ .*? # Non greedy match of any characters (including newlines)
+ \*/ # Closing "*/""",
+ re.MULTILINE | re.DOTALL | re.VERBOSE)
+
+ def __init__(self, source):
+ """Initialize a source.
+
+ Args:
+ source: str, The JavaScript source.
+ """
+
+ self.provides = set()
+ self.requires = set()
+
+ self._source = source
+ self._ScanSource()
+
+ def __str__(self):
+ return 'Source %s' % self._path
+
+ def GetSource(self):
+ """Get the source as a string."""
+ return self._source
+
+ @classmethod
+ def _StripComments(cls, source):
+ return cls._COMMENT_REGEX.sub('', source)
+
+ def _ScanSource(self):
+ """Fill in provides and requires by scanning the source."""
+
+ source = self._StripComments(self.GetSource())
+
+ source_lines = source.splitlines()
+ for line in source_lines:
+ match = _PROVIDE_REGEX.match(line)
+ if match:
+ self.provides.add(match.group(1))
+ match = _REQUIRES_REGEX.match(line)
+ if match:
+ self.requires.add(match.group(1))
+
+ # Closure's base file implicitly provides 'goog'.
+ for line in source_lines:
+ if line == _GOOG_BASE_LINE:
+ if len(self.provides) or len(self.requires):
+ raise Exception(
+ 'Base files should not provide or require namespaces.')
+ self.provides.add('goog')
+
+
+def GetFileContents(path):
+ """Get a file's contents as a string.
+
+ Args:
+ path: str, Path to file.
+
+ Returns:
+ str, Contents of file.
+
+ Raises:
+ IOError: An error occurred opening or reading the file.
+
+ """
+ fileobj = open(path)
+ try:
+ return fileobj.read()
+ finally:
+ fileobj.close()
diff --git a/lib/closure-library/closure/bin/build/treescan.py b/lib/closure-library/closure/bin/build/treescan.py
new file mode 100755
index 000000000000..6694593aab0a
--- /dev/null
+++ b/lib/closure-library/closure/bin/build/treescan.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# 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.
+
+
+"""Shared utility functions for scanning directory trees."""
+
+import os
+import re
+
+
+__author__ = 'nnaze@google.com (Nathan Naze)'
+
+
+# Matches a .js file path.
+_JS_FILE_REGEX = re.compile(r'^.+\.js$')
+
+
+def ScanTreeForJsFiles(root):
+ """Scans a directory tree for JavaScript files.
+
+ Args:
+ root: str, Path to a root directory.
+
+ Returns:
+ An iterable of paths to JS files, relative to cwd.
+ """
+ return ScanTree(root, path_filter=_JS_FILE_REGEX)
+
+
+def ScanTree(root, path_filter=None, ignore_hidden=True):
+ """Scans a directory tree for files.
+
+ Args:
+ root: str, Path to a root directory.
+ path_filter: A regular expression filter. If set, only paths matching
+ the path_filter are returned.
+ ignore_hidden: If True, do not follow or return hidden directories or files
+ (those starting with a '.' character).
+
+ Yields:
+ A string path to files, relative to cwd.
+ """
+
+ def OnError(os_error):
+ raise os_error
+
+ for dirpath, dirnames, filenames in os.walk(root, onerror=OnError):
+ # os.walk allows us to modify dirnames to prevent decent into particular
+ # directories. Avoid hidden directories.
+ for dirname in dirnames:
+ if ignore_hidden and dirname.startswith('.'):
+ dirnames.remove(dirname)
+
+ for filename in filenames:
+
+ # nothing that starts with '.'
+ if ignore_hidden and filename.startswith('.'):
+ continue
+
+ fullpath = os.path.join(dirpath, filename)
+
+ if path_filter and not path_filter.match(fullpath):
+ continue
+
+ yield os.path.normpath(fullpath)
diff --git a/lib/closure-library/closure/goog/base.js b/lib/closure-library/closure/goog/base.js
new file mode 100644
index 000000000000..dac4bbc3535b
--- /dev/null
+++ b/lib/closure-library/closure/goog/base.js
@@ -0,0 +1,1550 @@
+// BEGIN_FILE: goog/base.js
+// 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 Bootstrap for the Google JS Library (Closure).
+ *
+ * In uncompiled mode base.js will write out Closure's deps file, unless the
+ * global CLOSURE_NO_DEPS
is set to true. This allows projects to
+ * include their own deps file(s) from different locations.
+ *
+ *
+ * @provideGoog
+ */
+
+
+/**
+ * @define {boolean} Overridden to true by the compiler when --closure_pass
+ * or --mark_as_compiled is specified.
+ */
+var COMPILED = false;
+
+
+/**
+ * Base namespace for the Closure library. Checks to see goog is
+ * already defined in the current scope before assigning to prevent
+ * clobbering if base.js is loaded more than once.
+ *
+ * @const
+ */
+var goog = goog || {}; // Identifies this file as the Closure base.
+
+
+/**
+ * Reference to the global context. In most cases this will be 'window'.
+ */
+goog.global = this;
+
+
+/**
+ * @define {boolean} DEBUG is provided as a convenience so that debugging code
+ * that should not be included in a production js_binary can be easily stripped
+ * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
+ * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
+ * because they are generally used for debugging purposes and it is difficult
+ * for the JSCompiler to statically determine whether they are used.
+ */
+goog.DEBUG = true;
+
+
+/**
+ * @define {string} LOCALE defines the locale being used for compilation. It is
+ * used to select locale specific data to be compiled in js binary. BUILD rule
+ * can specify this value by "--define goog.LOCALE=" as JSCompiler
+ * option.
+ *
+ * Take into account that the locale code format is important. You should use
+ * the canonical Unicode format with hyphen as a delimiter. Language must be
+ * lowercase, Language Script - Capitalized, Region - UPPERCASE.
+ * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
+ *
+ * See more info about locale codes here:
+ * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
+ *
+ * For language codes you should use values defined by ISO 693-1. See it here
+ * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
+ * this rule: the Hebrew language. For legacy reasons the old code (iw) should
+ * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
+ */
+goog.LOCALE = 'en'; // default to en
+
+
+/**
+ * @define {boolean} Whether this code is running on trusted sites.
+ *
+ * On untrusted sites, several native functions can be defined or overridden by
+ * external libraries like Prototype, Datejs, and JQuery and setting this flag
+ * to false forces closure to use its own implementations when possible.
+ *
+ * If your javascript can be loaded by a third party site and you are wary about
+ * relying on non-standard implementations, specify
+ * "--define goog.TRUSTED_SITE=false" to the JSCompiler.
+ */
+goog.TRUSTED_SITE = true;
+
+
+/**
+ * Creates object stubs for a namespace. The presence of one or more
+ * goog.provide() calls indicate that the file defines the given
+ * objects/namespaces. Build tools also scan for provide/require statements
+ * to discern dependencies, build dependency files (see deps.js), etc.
+ * @see goog.require
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part".
+ */
+goog.provide = function(name) {
+ if (!COMPILED) {
+ // Ensure that the same namespace isn't provided twice. This is intended
+ // to teach new developers that 'goog.provide' is effectively a variable
+ // declaration. And when JSCompiler transforms goog.provide into a real
+ // variable declaration, the compiled JS should work the same as the raw
+ // JS--even when the raw JS uses goog.provide incorrectly.
+ if (goog.isProvided_(name)) {
+ throw Error('Namespace "' + name + '" already declared.');
+ }
+ delete goog.implicitNamespaces_[name];
+
+ var namespace = name;
+ while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+ if (goog.getObjectByName(namespace)) {
+ break;
+ }
+ goog.implicitNamespaces_[namespace] = true;
+ }
+ }
+
+ goog.exportPath_(name);
+};
+
+
+/**
+ * Marks that the current file should only be used for testing, and never for
+ * live code in production.
+ * @param {string=} opt_message Optional message to add to the error that's
+ * raised when used in production code.
+ */
+goog.setTestOnly = function(opt_message) {
+ if (COMPILED && !goog.DEBUG) {
+ opt_message = opt_message || '';
+ throw Error('Importing test-only code into non-debug environment' +
+ opt_message ? ': ' + opt_message : '.');
+ }
+};
+
+
+if (!COMPILED) {
+
+ /**
+ * Check if the given name has been goog.provided. This will return false for
+ * names that are available only as implicit namespaces.
+ * @param {string} name name of the object to look for.
+ * @return {boolean} Whether the name has been provided.
+ * @private
+ */
+ goog.isProvided_ = function(name) {
+ return !goog.implicitNamespaces_[name] && !!goog.getObjectByName(name);
+ };
+
+ /**
+ * Namespaces implicitly defined by goog.provide. For example,
+ * goog.provide('goog.events.Event') implicitly declares
+ * that 'goog' and 'goog.events' must be namespaces.
+ *
+ * @type {Object}
+ * @private
+ */
+ goog.implicitNamespaces_ = {};
+}
+
+
+/**
+ * Builds an object structure for the provided namespace path,
+ * ensuring that names that already exist are not overwritten. For
+ * example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * Used by goog.provide and goog.exportSymbol.
+ * @param {string} name name of the object that this file defines.
+ * @param {*=} opt_object the object to expose at the end of the path.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ * is |goog.global|.
+ * @private
+ */
+goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split('.');
+ var cur = opt_objectToExportTo || goog.global;
+
+ // Internet Explorer exhibits strange behavior when throwing errors from
+ // methods externed in this manner. See the testExportSymbolExceptions in
+ // base_test.html for an example.
+ if (!(parts[0] in cur) && cur.execScript) {
+ cur.execScript('var ' + parts[0]);
+ }
+
+ // Certain browsers cannot parse code in the form for((a in b); c;);
+ // This pattern is produced by the JSCompiler when it collapses the
+ // statement above into the conditional loop below. To prevent this from
+ // happening, use a for-loop and reserve the init logic as below.
+
+ // Parentheses added to eliminate strict JS warning in Firefox.
+ for (var part; parts.length && (part = parts.shift());) {
+ if (!parts.length && goog.isDef(opt_object)) {
+ // last part and we have an object; use it
+ cur[part] = opt_object;
+ } else if (cur[part]) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+};
+
+
+/**
+ * Returns an object based on its fully qualified external name. If you are
+ * using a compilation pass that renames property names beware that using this
+ * function will not find renamed properties.
+ *
+ * @param {string} name The fully qualified name.
+ * @param {Object=} opt_obj The object within which to look; default is
+ * |goog.global|.
+ * @return {?} The value (object or primitive) or, if not found, null.
+ */
+goog.getObjectByName = function(name, opt_obj) {
+ var parts = name.split('.');
+ var cur = opt_obj || goog.global;
+ for (var part; part = parts.shift(); ) {
+ if (goog.isDefAndNotNull(cur[part])) {
+ cur = cur[part];
+ } else {
+ return null;
+ }
+ }
+ return cur;
+};
+
+
+/**
+ * Globalizes a whole namespace, such as goog or goog.lang.
+ *
+ * @param {Object} obj The namespace to globalize.
+ * @param {Object=} opt_global The object to add the properties to.
+ * @deprecated Properties may be explicitly exported to the global scope, but
+ * this should no longer be done in bulk.
+ */
+goog.globalize = function(obj, opt_global) {
+ var global = opt_global || goog.global;
+ for (var x in obj) {
+ global[x] = obj[x];
+ }
+};
+
+
+/**
+ * Adds a dependency from a file to the files it requires.
+ * @param {string} relPath The path to the js file.
+ * @param {Array} provides An array of strings with the names of the objects
+ * this file provides.
+ * @param {Array} requires An array of strings with the names of the objects
+ * this file requires.
+ */
+goog.addDependency = function(relPath, provides, requires) {
+ if (!COMPILED) {
+ var provide, require;
+ var path = relPath.replace(/\\/g, '/');
+ var deps = goog.dependencies_;
+ for (var i = 0; provide = provides[i]; i++) {
+ deps.nameToPath[provide] = path;
+ if (!(path in deps.pathToNames)) {
+ deps.pathToNames[path] = {};
+ }
+ deps.pathToNames[path][provide] = true;
+ }
+ for (var j = 0; require = requires[j]; j++) {
+ if (!(path in deps.requires)) {
+ deps.requires[path] = {};
+ }
+ deps.requires[path][require] = true;
+ }
+ }
+};
+
+
+
+
+// NOTE(nnaze): The debug DOM loader was included in base.js as an orignal
+// way to do "debug-mode" development. The dependency system can sometimes
+// be confusing, as can the debug DOM loader's asyncronous nature.
+//
+// With the DOM loader, a call to goog.require() is not blocking -- the
+// script will not load until some point after the current script. If a
+// namespace is needed at runtime, it needs to be defined in a previous
+// script, or loaded via require() with its registered dependencies.
+// User-defined namespaces may need their own deps file. See http://go/js_deps,
+// http://go/genjsdeps, or, externally, DepsWriter.
+// http://code.google.com/closure/library/docs/depswriter.html
+//
+// Because of legacy clients, the DOM loader can't be easily removed from
+// base.js. Work is being done to make it disableable or replaceable for
+// different environments (DOM-less JavaScript interpreters like Rhino or V8,
+// for example). See bootstrap/ for more information.
+
+
+/**
+ * @define {boolean} Whether to enable the debug loader.
+ *
+ * If enabled, a call to goog.require() will attempt to load the namespace by
+ * appending a script tag to the DOM (if the namespace has been registered).
+ *
+ * If disabled, goog.require() will simply assert that the namespace has been
+ * provided (and depend on the fact that some outside tool correctly ordered
+ * the script).
+ */
+goog.ENABLE_DEBUG_LOADER = true;
+
+
+/**
+ * Implements a system for the dynamic resolution of dependencies
+ * that works in parallel with the BUILD system. Note that all calls
+ * to goog.require will be stripped by the JSCompiler when the
+ * --closure_pass option is used.
+ * @see goog.provide
+ * @param {string} name Namespace to include (as was given in goog.provide())
+ * in the form "goog.package.part".
+ */
+goog.require = function(name) {
+
+ // if the object already exists we do not need do do anything
+ // TODO(arv): If we start to support require based on file name this has
+ // to change
+ // TODO(arv): If we allow goog.foo.* this has to change
+ // TODO(arv): If we implement dynamic load after page load we should probably
+ // not remove this code for the compiled output
+ if (!COMPILED) {
+ if (goog.isProvided_(name)) {
+ return;
+ }
+
+ if (goog.ENABLE_DEBUG_LOADER) {
+ var path = goog.getPathFromDeps_(name);
+ if (path) {
+ goog.included_[path] = true;
+ goog.writeScripts_();
+ return;
+ }
+ }
+
+ var errorMessage = 'goog.require could not find: ' + name;
+ if (goog.global.console) {
+ goog.global.console['error'](errorMessage);
+ }
+
+
+ throw Error(errorMessage);
+
+ }
+};
+
+
+/**
+ * Path for included scripts
+ * @type {string}
+ */
+goog.basePath = '';
+
+
+/**
+ * A hook for overriding the base path.
+ * @type {string|undefined}
+ */
+goog.global.CLOSURE_BASE_PATH;
+
+
+/**
+ * Whether to write out Closure's deps file. By default,
+ * the deps are written.
+ * @type {boolean|undefined}
+ */
+goog.global.CLOSURE_NO_DEPS;
+
+
+/**
+ * A function to import a single script. This is meant to be overridden when
+ * Closure is being run in non-HTML contexts, such as web workers. It's defined
+ * in the global scope so that it can be set before base.js is loaded, which
+ * allows deps.js to be imported properly.
+ *
+ * The function is passed the script source, which is a relative URI. It should
+ * return true if the script was imported, false otherwise.
+ */
+goog.global.CLOSURE_IMPORT_SCRIPT;
+
+
+/**
+ * Null function used for default values of callbacks, etc.
+ * @return {void} Nothing.
+ */
+goog.nullFunction = function() {};
+
+
+/**
+ * The identity function. Returns its first argument.
+ *
+ * @param {*=} opt_returnValue The single value that will be returned.
+ * @param {...*} var_args Optional trailing arguments. These are ignored.
+ * @return {?} The first argument. We can't know the type -- just pass it along
+ * without type.
+ * @deprecated Use goog.functions.identity instead.
+ */
+goog.identityFunction = function(opt_returnValue, var_args) {
+ return opt_returnValue;
+};
+
+
+/**
+ * When defining a class Foo with an abstract method bar(), you can do:
+ *
+ * Foo.prototype.bar = goog.abstractMethod
+ *
+ * Now if a subclass of Foo fails to override bar(), an error
+ * will be thrown when bar() is invoked.
+ *
+ * Note: This does not take the name of the function to override as
+ * an argument because that would make it more difficult to obfuscate
+ * our JavaScript code.
+ *
+ * @type {!Function}
+ * @throws {Error} when invoked to indicate the method should be
+ * overridden.
+ */
+goog.abstractMethod = function() {
+ throw Error('unimplemented abstract method');
+};
+
+
+/**
+ * Adds a {@code getInstance} static method that always return the same instance
+ * object.
+ * @param {!Function} ctor The constructor for the class to add the static
+ * method to.
+ */
+goog.addSingletonGetter = function(ctor) {
+ ctor.getInstance = function() {
+ if (ctor.instance_) {
+ return ctor.instance_;
+ }
+ if (goog.DEBUG) {
+ // NOTE: JSCompiler can't optimize away Array#push.
+ goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
+ }
+ return ctor.instance_ = new ctor;
+ };
+};
+
+
+/**
+ * All singleton classes that have been instantiated, for testing. Don't read
+ * it directly, use the {@code goog.testing.singleton} module. The compiler
+ * removes this variable if unused.
+ * @type {!Array.}
+ * @private
+ */
+goog.instantiatedSingletons_ = [];
+
+
+if (!COMPILED && goog.ENABLE_DEBUG_LOADER) {
+ /**
+ * Object used to keep track of urls that have already been added. This
+ * record allows the prevention of circular dependencies.
+ * @type {Object}
+ * @private
+ */
+ goog.included_ = {};
+
+
+ /**
+ * This object is used to keep track of dependencies and other data that is
+ * used for loading scripts
+ * @private
+ * @type {Object}
+ */
+ goog.dependencies_ = {
+ pathToNames: {}, // 1 to many
+ nameToPath: {}, // 1 to 1
+ requires: {}, // 1 to many
+ // used when resolving dependencies to prevent us from
+ // visiting the file twice
+ visited: {},
+ written: {} // used to keep track of script files we have written
+ };
+
+
+ /**
+ * Tries to detect whether is in the context of an HTML document.
+ * @return {boolean} True if it looks like HTML document.
+ * @private
+ */
+ goog.inHtmlDocument_ = function() {
+ var doc = goog.global.document;
+ return typeof doc != 'undefined' &&
+ 'write' in doc; // XULDocument misses write.
+ };
+
+
+ /**
+ * Tries to detect the base path of the base.js script that bootstraps Closure
+ * @private
+ */
+ goog.findBasePath_ = function() {
+ if (goog.global.CLOSURE_BASE_PATH) {
+ goog.basePath = goog.global.CLOSURE_BASE_PATH;
+ return;
+ } else if (!goog.inHtmlDocument_()) {
+ return;
+ }
+ var doc = goog.global.document;
+ var scripts = doc.getElementsByTagName('script');
+ // Search backwards since the current script is in almost all cases the one
+ // that has base.js.
+ for (var i = scripts.length - 1; i >= 0; --i) {
+ var src = scripts[i].src;
+ var qmark = src.lastIndexOf('?');
+ var l = qmark == -1 ? src.length : qmark;
+ if (src.substr(l - 7, 7) == 'base.js') {
+ goog.basePath = src.substr(0, l - 7);
+ return;
+ }
+ }
+ };
+
+
+ /**
+ * Imports a script if, and only if, that script hasn't already been imported.
+ * (Must be called at execution time)
+ * @param {string} src Script source.
+ * @private
+ */
+ goog.importScript_ = function(src) {
+ var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
+ goog.writeScriptTag_;
+ if (!goog.dependencies_.written[src] && importScript(src)) {
+ goog.dependencies_.written[src] = true;
+ }
+ };
+
+
+ /**
+ * The default implementation of the import function. Writes a script tag to
+ * import the script.
+ *
+ * @param {string} src The script source.
+ * @return {boolean} True if the script was imported, false otherwise.
+ * @private
+ */
+ goog.writeScriptTag_ = function(src) {
+ if (goog.inHtmlDocument_()) {
+ var doc = goog.global.document;
+
+ // If the user tries to require a new symbol after document load,
+ // something has gone terribly wrong. Doing a document.write would
+ // wipe out the page.
+ if (doc.readyState == 'complete') {
+ // Certain test frameworks load base.js multiple times, which tries
+ // to write deps.js each time. If that happens, just fail silently.
+ // These frameworks wipe the page between each load of base.js, so this
+ // is OK.
+ var isDeps = /\bdeps.js$/.test(src);
+ if (isDeps) {
+ return false;
+ } else {
+ throw Error('Cannot write "' + src + '" after document load');
+ }
+ }
+
+ doc.write(
+ '