mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
chore(Rakefile): get ready for modules
This commit is contained in:
116
Rakefile
116
Rakefile
@@ -2,7 +2,7 @@ require 'yaml'
|
||||
include FileUtils
|
||||
|
||||
content = File.open('angularFiles.js', 'r') {|f| f.read }
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').gsub(/angularFiles = /, '').gsub(/:/, '=>'));
|
||||
files = eval(content.gsub(/\};(\s|\S)*/, '}').gsub(/angularFiles = /, '').gsub(/:/, '=>').gsub(/\/\//, '#'));
|
||||
|
||||
BUILD_DIR = 'build'
|
||||
|
||||
@@ -34,38 +34,24 @@ end
|
||||
|
||||
desc 'Compile Scenario'
|
||||
task :compile_scenario => :init do
|
||||
|
||||
deps = [
|
||||
|
||||
concatFile('angular-scenario.js', [
|
||||
'lib/jquery/jquery.js',
|
||||
'src/ngScenario/angular.prefix',
|
||||
files['angularSrc'],
|
||||
files['angularScenario'],
|
||||
'src/ngScenario/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('angular-scenario.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
f.write(gen_css('css/angular.css') + "\n")
|
||||
f.write(gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
|
||||
end
|
||||
|
||||
desc 'Compile JSTD Scenario Adapter'
|
||||
task :compile_jstd_scenario_adapter => :init do
|
||||
|
||||
deps = [
|
||||
concatFile('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
]
|
||||
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
File.open(path_to('jstd-scenario-adapter.js'), 'w') do |f|
|
||||
f.write(%x{#{concat}}.gsub('"NG_VERSION_FULL"', NG_VERSION.full))
|
||||
end
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
@@ -80,55 +66,24 @@ end
|
||||
desc 'Compile JavaScript'
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter] do
|
||||
|
||||
deps = [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
]
|
||||
|
||||
File.open(path_to('angular.js'), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
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
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(gen_css('css/angular.css', true))
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular.js')} \
|
||||
--js_output_file #{path_to('angular.min.js')})
|
||||
concatFile('angular.js', [
|
||||
'src/angular.prefix',
|
||||
files['angularSrc'],
|
||||
'src/angular.suffix',
|
||||
], gen_css('css/angular.css', true))
|
||||
|
||||
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
|
||||
|
||||
File.open(path_to('angular-loader.js'), 'w') do |f|
|
||||
concat = 'cat ' + [
|
||||
concatFile('angular-loader.js', [
|
||||
'src/loader.prefix',
|
||||
'src/loader.js',
|
||||
'src/loader.suffix'].flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
gsub(/^\s*['"]use strict['"];?\s*$/, '') # remove all file-specific strict mode flags
|
||||
|
||||
f.write(content)
|
||||
end
|
||||
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to('angular-loader.js')} \
|
||||
--js_output_file #{path_to('angular-loader.min.js')})
|
||||
'src/loader.suffix'])
|
||||
|
||||
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
|
||||
|
||||
|
||||
closureCompile('angular.js')
|
||||
closureCompile('angular-loader.js')
|
||||
|
||||
end
|
||||
|
||||
@@ -153,11 +108,11 @@ task :package => [:clean, :compile, :docs] do
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
['src/ngMock/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular-loader.js'),
|
||||
[ path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-loader.js'),
|
||||
path_to('angular-loader.min.js'),
|
||||
path_to('angular-mocks.js'),
|
||||
path_to('angular-scenario.js'),
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
@@ -336,3 +291,32 @@ end
|
||||
def path_to(filename)
|
||||
return File.join(BUILD_DIR, *filename)
|
||||
end
|
||||
|
||||
def closureCompile(filename)
|
||||
puts "Compiling #{filename} ..."
|
||||
%x(java -jar lib/closure-compiler/compiler.jar \
|
||||
--compilation_level SIMPLE_OPTIMIZATIONS \
|
||||
--language_in ECMASCRIPT5_STRICT \
|
||||
--js #{path_to(filename)} \
|
||||
--js_output_file #{path_to(filename.gsub(/\.js$/, '.min.js'))})
|
||||
end
|
||||
|
||||
def concatFile(filename, deps, footer='')
|
||||
puts "Building #{filename} ..."
|
||||
File.open(path_to(filename), 'w') do |f|
|
||||
concat = 'cat ' + deps.flatten.join(' ')
|
||||
|
||||
content = %x{#{concat}}.
|
||||
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
|
||||
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
|
||||
gsub(/'USE STRICT'/, "'use strict'") # rename the placeholder in angular.prefix
|
||||
|
||||
f.write(content)
|
||||
f.write(footer)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
90
angularFiles.js
vendored
90
angularFiles.js
vendored
@@ -66,6 +66,10 @@ angularFiles = {
|
||||
'src/ng/directive/style.js'
|
||||
],
|
||||
|
||||
'angularSrcModules': [
|
||||
'src/ngMock/angular-mocks.js'
|
||||
],
|
||||
|
||||
'angularScenario': [
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/Application.js',
|
||||
@@ -83,20 +87,9 @@ angularFiles = {
|
||||
'src/ngScenario/output/Object.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'example/personalLog/*.js',
|
||||
'angularTest': [
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/output/*.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'src/ngScenario/*.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
@@ -105,7 +98,21 @@ angularFiles = {
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
@@ -122,19 +129,20 @@ angularFiles = {
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
'jstdMocks': [
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'build/angular.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'test/matchers.js',
|
||||
'test/ngMock/angular-mocksSpec.js'
|
||||
'test/ngMock/*.js',
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'angularSrc',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
@@ -152,23 +160,12 @@ angularFiles = {
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'test/testabilityPatch.js',
|
||||
'test/matchers.js',
|
||||
'src/ngScenario/Scenario.js',
|
||||
'src/ngScenario/output/*.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'src/ngScenario/*.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/ng/*.js',
|
||||
'test/ng/directive/*.js',
|
||||
'test/ng/filter/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
@@ -181,15 +178,30 @@ angularFiles = {
|
||||
|
||||
// Execute only in slim-jim
|
||||
if (typeof JASMINE_ADAPTER !== 'undefined') {
|
||||
// SlimJim config
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
angularFiles.jstd.forEach(function(pattern) {
|
||||
// replace angular source
|
||||
if (pattern === '@angularSrc') files = files.concat(angularFiles.angularSrc);
|
||||
// ignore jstd and jasmine files
|
||||
else if (!/jstd|jasmine/.test(pattern)) files.push(pattern);
|
||||
// Testacular config
|
||||
var mergedFiles = [];
|
||||
angularFiles.jstd.forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
if (!deps) {
|
||||
console.log('No dependency:' + file)
|
||||
}
|
||||
mergedFiles = mergedFiles.concat(deps);
|
||||
} else {
|
||||
mergedFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
files = [JASMINE, JASMINE_ADAPTER];
|
||||
|
||||
mergedFiles.forEach(function(file){
|
||||
if (/jstd|jasmine/.test(file)) return;
|
||||
files.push(file);
|
||||
});
|
||||
|
||||
|
||||
exclude = angularFiles.jstdExclude;
|
||||
|
||||
autoWatch = true;
|
||||
|
||||
@@ -16,7 +16,7 @@ fs.readFile('angularFiles.js', function(err, data) {
|
||||
fs.writeFile('./jsTestDriver.conf', prefix + combine(angularFiles.jstd,
|
||||
angularFiles.jstdExclude));
|
||||
|
||||
fs.writeFile('./jsTestDriver-mocks.conf', prefix + combine(angularFiles.jstdMocks));
|
||||
fs.writeFile('./jsTestDriver-modules.conf', prefix + combine(angularFiles.jstdModules));
|
||||
|
||||
fs.writeFile('./jsTestDriver-scenario.conf', prefixScenario +
|
||||
combine(angularFiles.jstdScenario) +
|
||||
@@ -40,6 +40,8 @@ function combine(load, exclude) {
|
||||
if (exclude) fileList += ('\n\nexclude:\n- ' + exclude.join('\n- '));
|
||||
|
||||
//Replace placeholders for src list before returning
|
||||
return fileList.replace(/@angularSrc/g, angularSrc);
|
||||
return fileList.replace(/@(.*)/g, function(all, alias) {
|
||||
return angularFiles[alias].join('\n- ');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ function publishExternalAPI(angular){
|
||||
$location: $LocationProvider,
|
||||
$log: $LogProvider,
|
||||
$parse: $ParseProvider,
|
||||
$resource: $ResourceProvider,
|
||||
$route: $RouteProvider,
|
||||
$routeParams: $RouteParamsProvider,
|
||||
$rootScope: $RootScopeProvider,
|
||||
|
||||
@@ -152,7 +152,7 @@ function $HttpProvider() {
|
||||
* For unit testing applications that use `$http` service, see
|
||||
* {@link angular.module.ngMock.$httpBackend $httpBackend mock}.
|
||||
*
|
||||
* For a higher level of abstraction, please check out the {@link angular.module.ng.$resource
|
||||
* For a higher level of abstraction, please check out the {@link angular.module.ngResource.$resource
|
||||
* $resource} service.
|
||||
*
|
||||
* The $http API is based on the {@link angular.module.ng.$q deferred/promise APIs} exposed by
|
||||
|
||||
@@ -18,7 +18,7 @@ var XHR = window.XMLHttpRequest || function() {
|
||||
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
|
||||
*
|
||||
* You should never need to use this service directly, instead use the higher-level abstractions:
|
||||
* {@link angular.module.ng.$http $http} or {@link angular.module.ng.$resource $resource}.
|
||||
* {@link angular.module.ng.$http $http} or {@link angular.module.ngResource.$resource $resource}.
|
||||
*
|
||||
* During testing this implementation is swapped with {@link angular.module.ngMock.$httpBackend mock
|
||||
* $httpBackend} which can be trained with responses.
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.module.ngResource
|
||||
* @description
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.module.ng.$resource
|
||||
* @name angular.module.ngResource.$resource
|
||||
* @requires $http
|
||||
*
|
||||
* @description
|
||||
@@ -200,8 +206,8 @@
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
function $ResourceProvider() {
|
||||
this.$get = ['$http', function($http) {
|
||||
angular.module('ngResource', ['ng']).
|
||||
factory('$resource', ['$http', '$parse', function($http, $parse) {
|
||||
var DEFAULT_ACTIONS = {
|
||||
'get': {method:'GET'},
|
||||
'save': {method:'POST'},
|
||||
@@ -209,9 +215,54 @@ function $ResourceProvider() {
|
||||
'remove': {method:'DELETE'},
|
||||
'delete': {method:'DELETE'}
|
||||
};
|
||||
var forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
isFunction = angular.isFunction,
|
||||
getter = function(obj, path) {
|
||||
return $parse(path)(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriSegment(val) {
|
||||
return encodeUriQuery(val, true).
|
||||
replace(/%26/gi, '&').
|
||||
replace(/%3D/gi, '=').
|
||||
replace(/%2B/gi, '+');
|
||||
}
|
||||
|
||||
|
||||
function Route(template, defaults) {
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
|
||||
* method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
|
||||
* encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace((pctEncodeSpaces ? null : /%20/g), '+');
|
||||
}
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template = template + '#';
|
||||
this.defaults = defaults || {};
|
||||
var urlParams = this.urlParams = {};
|
||||
@@ -236,11 +287,12 @@ function $ResourceProvider() {
|
||||
});
|
||||
url = url.replace(/\/?#$/, '');
|
||||
var query = [];
|
||||
forEachSorted(params, function(value, key){
|
||||
forEach(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
|
||||
}
|
||||
});
|
||||
query.sort();
|
||||
url = url.replace(/\/*$/, '');
|
||||
return url + (query.length ? '?' + query.join('&') : '');
|
||||
}
|
||||
@@ -364,5 +416,4 @@ function $ResourceProvider() {
|
||||
}
|
||||
|
||||
return ResourceFactory;
|
||||
}];
|
||||
}
|
||||
}]);
|
||||
3
src/publishExternalApis.js
Normal file
3
src/publishExternalApis.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
publishExternalAPI(angular);
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
if [ ! -e test.dissable ]; then
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --tests all --config jsTestDriver-mocks.conf $@
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --tests all --config jsTestDriver-modules.conf $@
|
||||
fi
|
||||
3
test/ngMock/angular-mocksSpec.js
vendored
3
test/ngMock/angular-mocksSpec.js
vendored
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
describe('ngMock', function() {
|
||||
var noop = angular.noop;
|
||||
|
||||
describe('$browser', function() {
|
||||
|
||||
@@ -8,7 +9,7 @@ describe('ngMock', function() {
|
||||
|
||||
it('should store url, done', inject(function($browser) {
|
||||
var url = 'some.js',
|
||||
done = noop;
|
||||
done = angular.noop;
|
||||
|
||||
$browser.addJs(url, done);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
describe("resource", function() {
|
||||
var $resource, CreditCard, callback, $httpBackend;
|
||||
|
||||
beforeEach(module('ngResource'));
|
||||
beforeEach(inject(function($injector) {
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
$resource = $injector.get('$resource');
|
||||
@@ -262,7 +263,7 @@ describe("resource", function() {
|
||||
$httpBackend.flush();
|
||||
expect(cc instanceof CreditCard).toBe(true);
|
||||
|
||||
$httpBackend.expect('POST', '/CreditCard/123', toJson(data)).respond('');
|
||||
$httpBackend.expect('POST', '/CreditCard/123', angular.toJson(data)).respond('');
|
||||
var idBefore = cc.id;
|
||||
|
||||
cc.$save();
|
||||
@@ -8,8 +8,6 @@
|
||||
*/
|
||||
_jQuery.event.special.change = undefined;
|
||||
|
||||
|
||||
publishExternalAPI(angular);
|
||||
bindJQuery();
|
||||
beforeEach(function() {
|
||||
publishExternalAPI(angular);
|
||||
|
||||
Reference in New Issue
Block a user