mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tech: updated angular libs
This commit is contained in:
1
src/vendor/angular/angular-dragdrop.js
vendored
1
src/vendor/angular/angular-dragdrop.js
vendored
@@ -39,6 +39,7 @@ angular.module("ang-drag-drop",[])
|
||||
element.attr("draggable", false);
|
||||
|
||||
attrs.$observe("uiDraggable", function (newValue) {
|
||||
console.log(newValue);
|
||||
if(newValue){
|
||||
element.attr("draggable", newValue);
|
||||
}
|
||||
|
||||
233
src/vendor/angular/angular-mocks.js
vendored
233
src/vendor/angular/angular-mocks.js
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @license AngularJS v1.3.0-rc.3
|
||||
* @license AngularJS v1.3.4
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
@@ -53,9 +53,10 @@ angular.mock.$Browser = function() {
|
||||
self.onUrlChange = function(listener) {
|
||||
self.pollFns.push(
|
||||
function() {
|
||||
if (self.$$lastUrl != self.$$url) {
|
||||
if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
|
||||
self.$$lastUrl = self.$$url;
|
||||
listener(self.$$url);
|
||||
self.$$lastState = self.$$state;
|
||||
listener(self.$$url, self.$$state);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -73,7 +74,7 @@ angular.mock.$Browser = function() {
|
||||
self.defer = function(fn, delay) {
|
||||
delay = delay || 0;
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
|
||||
self.deferredFns.sort(function(a,b){ return a.time - b.time;});
|
||||
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
|
||||
return self.deferredNextId++;
|
||||
};
|
||||
|
||||
@@ -116,7 +117,7 @@ angular.mock.$Browser = function() {
|
||||
self.defer.now += delay;
|
||||
} else {
|
||||
if (self.deferredFns.length) {
|
||||
self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
|
||||
self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
|
||||
} else {
|
||||
throw new Error('No deferred tasks to be flushed');
|
||||
}
|
||||
@@ -141,7 +142,7 @@ angular.mock.$Browser.prototype = {
|
||||
* run all fns in pollFns
|
||||
*/
|
||||
poll: function poll() {
|
||||
angular.forEach(this.pollFns, function(pollFn){
|
||||
angular.forEach(this.pollFns, function(pollFn) {
|
||||
pollFn();
|
||||
});
|
||||
},
|
||||
@@ -151,15 +152,24 @@ angular.mock.$Browser.prototype = {
|
||||
return pollFn;
|
||||
},
|
||||
|
||||
url: function(url, replace) {
|
||||
url: function(url, replace, state) {
|
||||
if (angular.isUndefined(state)) {
|
||||
state = null;
|
||||
}
|
||||
if (url) {
|
||||
this.$$url = url;
|
||||
// Native pushState serializes & copies the object; simulate it.
|
||||
this.$$state = angular.copy(state);
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.$$url;
|
||||
},
|
||||
|
||||
state: function() {
|
||||
return this.$$state;
|
||||
},
|
||||
|
||||
cookies: function(name, value) {
|
||||
if (name) {
|
||||
if (angular.isUndefined(value)) {
|
||||
@@ -191,7 +201,7 @@ angular.mock.$Browser.prototype = {
|
||||
*
|
||||
* @description
|
||||
* Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
|
||||
* passed into the `$exceptionHandler`.
|
||||
* passed to the `$exceptionHandler`.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -200,7 +210,7 @@ angular.mock.$Browser.prototype = {
|
||||
*
|
||||
* @description
|
||||
* Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
|
||||
* into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
|
||||
* to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
|
||||
* information.
|
||||
*
|
||||
*
|
||||
@@ -240,16 +250,15 @@ angular.mock.$ExceptionHandlerProvider = function() {
|
||||
*
|
||||
* @param {string} mode Mode of operation, defaults to `rethrow`.
|
||||
*
|
||||
* - `rethrow`: If any errors are passed into the handler in tests, it typically
|
||||
* means that there is a bug in the application or test, so this mock will
|
||||
* make these tests fail.
|
||||
* - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
|
||||
* is a bug in the application or test, so this mock will make these tests fail.
|
||||
* - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
|
||||
* mode stores an array of errors in `$exceptionHandler.errors`, to allow later
|
||||
* assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
|
||||
* {@link ngMock.$log#reset reset()}
|
||||
*/
|
||||
this.mode = function(mode) {
|
||||
switch(mode) {
|
||||
switch (mode) {
|
||||
case 'rethrow':
|
||||
handler = function(e) {
|
||||
throw e;
|
||||
@@ -307,7 +316,7 @@ angular.mock.$LogProvider = function() {
|
||||
}
|
||||
};
|
||||
|
||||
this.$get = function () {
|
||||
this.$get = function() {
|
||||
var $log = {
|
||||
log: function() { $log.log.logs.push(concat([], arguments, 0)); },
|
||||
warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
|
||||
@@ -327,13 +336,13 @@ angular.mock.$LogProvider = function() {
|
||||
* @description
|
||||
* Reset all of the logging arrays to empty.
|
||||
*/
|
||||
$log.reset = function () {
|
||||
$log.reset = function() {
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $log#log.logs
|
||||
*
|
||||
* @description
|
||||
* Array of messages logged using {@link ngMock.$log#log}.
|
||||
* Array of messages logged using {@link ng.$log#log `log()`}.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
@@ -347,7 +356,7 @@ angular.mock.$LogProvider = function() {
|
||||
* @name $log#info.logs
|
||||
*
|
||||
* @description
|
||||
* Array of messages logged using {@link ngMock.$log#info}.
|
||||
* Array of messages logged using {@link ng.$log#info `info()`}.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
@@ -361,7 +370,7 @@ angular.mock.$LogProvider = function() {
|
||||
* @name $log#warn.logs
|
||||
*
|
||||
* @description
|
||||
* Array of messages logged using {@link ngMock.$log#warn}.
|
||||
* Array of messages logged using {@link ng.$log#warn `warn()`}.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
@@ -375,7 +384,7 @@ angular.mock.$LogProvider = function() {
|
||||
* @name $log#error.logs
|
||||
*
|
||||
* @description
|
||||
* Array of messages logged using {@link ngMock.$log#error}.
|
||||
* Array of messages logged using {@link ng.$log#error `error()`}.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
@@ -389,7 +398,7 @@ angular.mock.$LogProvider = function() {
|
||||
* @name $log#debug.logs
|
||||
*
|
||||
* @description
|
||||
* Array of messages logged using {@link ngMock.$log#debug}.
|
||||
* Array of messages logged using {@link ng.$log#debug `debug()`}.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
@@ -405,21 +414,21 @@ angular.mock.$LogProvider = function() {
|
||||
* @name $log#assertEmpty
|
||||
*
|
||||
* @description
|
||||
* Assert that the all of the logging methods have no logged messages. If messages present, an
|
||||
* exception is thrown.
|
||||
* Assert that all of the logging methods have no logged messages. If any messages are present,
|
||||
* an exception is thrown.
|
||||
*/
|
||||
$log.assertEmpty = function() {
|
||||
var errors = [];
|
||||
angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
|
||||
angular.forEach($log[logLevel].logs, function(log) {
|
||||
angular.forEach(log, function (logItem) {
|
||||
angular.forEach(log, function(logItem) {
|
||||
errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
|
||||
(logItem.stack || ''));
|
||||
});
|
||||
});
|
||||
});
|
||||
if (errors.length) {
|
||||
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
|
||||
errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
|
||||
"an expected log message was not checked and removed:");
|
||||
errors.push('');
|
||||
throw new Error(errors.join('\n---------\n'));
|
||||
@@ -452,17 +461,17 @@ angular.mock.$LogProvider = function() {
|
||||
* @returns {promise} A promise which will be notified on each iteration.
|
||||
*/
|
||||
angular.mock.$IntervalProvider = function() {
|
||||
this.$get = ['$rootScope', '$q',
|
||||
function($rootScope, $q) {
|
||||
this.$get = ['$browser', '$rootScope', '$q', '$$q',
|
||||
function($browser, $rootScope, $q, $$q) {
|
||||
var repeatFns = [],
|
||||
nextRepeatId = 0,
|
||||
now = 0;
|
||||
|
||||
var $interval = function(fn, delay, count, invokeApply) {
|
||||
var deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
iteration = 0,
|
||||
skipApply = (angular.isDefined(invokeApply) && !invokeApply);
|
||||
var iteration = 0,
|
||||
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
|
||||
deferred = (skipApply ? $$q : $q).defer(),
|
||||
promise = deferred.promise;
|
||||
|
||||
count = (angular.isDefined(count)) ? count : 0;
|
||||
promise.then(null, null, fn);
|
||||
@@ -485,7 +494,11 @@ angular.mock.$IntervalProvider = function() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipApply) $rootScope.$apply();
|
||||
if (skipApply) {
|
||||
$browser.defer.flush();
|
||||
} else {
|
||||
$rootScope.$apply();
|
||||
}
|
||||
}
|
||||
|
||||
repeatFns.push({
|
||||
@@ -495,7 +508,7 @@ angular.mock.$IntervalProvider = function() {
|
||||
id: nextRepeatId,
|
||||
deferred: deferred
|
||||
});
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
|
||||
|
||||
nextRepeatId++;
|
||||
return promise;
|
||||
@@ -511,7 +524,7 @@ angular.mock.$IntervalProvider = function() {
|
||||
* @returns {boolean} Returns `true` if the task was successfully cancelled.
|
||||
*/
|
||||
$interval.cancel = function(promise) {
|
||||
if(!promise) return false;
|
||||
if (!promise) return false;
|
||||
var fnIndex;
|
||||
|
||||
angular.forEach(repeatFns, function(fn, index) {
|
||||
@@ -544,7 +557,7 @@ angular.mock.$IntervalProvider = function() {
|
||||
var task = repeatFns[0];
|
||||
task.fn();
|
||||
task.nextTime += task.delay;
|
||||
repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
|
||||
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
|
||||
}
|
||||
return millis;
|
||||
};
|
||||
@@ -572,10 +585,10 @@ function jsonStringToDate(string) {
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
date.setUTCHours(int(match[4]||0) - tzHour,
|
||||
int(match[5]||0) - tzMin,
|
||||
int(match[6]||0),
|
||||
int(match[7]||0));
|
||||
date.setUTCHours(int(match[4] || 0) - tzHour,
|
||||
int(match[5] || 0) - tzMin,
|
||||
int(match[6] || 0),
|
||||
int(match[7] || 0));
|
||||
return date;
|
||||
}
|
||||
return string;
|
||||
@@ -592,7 +605,7 @@ function padNumber(num, digits, trim) {
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while(num.length < digits) num = '0' + num;
|
||||
while (num.length < digits) num = '0' + num;
|
||||
if (trim)
|
||||
num = num.substr(num.length - digits);
|
||||
return neg + num;
|
||||
@@ -636,7 +649,7 @@ function padNumber(num, digits, trim) {
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
angular.mock.TzDate = function(offset, timestamp) {
|
||||
var self = new Date(0);
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
@@ -654,7 +667,7 @@ angular.mock.TzDate = function (offset, timestamp) {
|
||||
}
|
||||
|
||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
||||
self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
|
||||
self.date = new Date(timestamp + self.offsetDiff);
|
||||
|
||||
self.getTime = function() {
|
||||
@@ -779,20 +792,20 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
|
||||
function($delegate, $$asyncCallback, $timeout, $browser) {
|
||||
var animate = {
|
||||
queue : [],
|
||||
cancel : $delegate.cancel,
|
||||
enabled : $delegate.enabled,
|
||||
triggerCallbackEvents : function() {
|
||||
queue: [],
|
||||
cancel: $delegate.cancel,
|
||||
enabled: $delegate.enabled,
|
||||
triggerCallbackEvents: function() {
|
||||
$$asyncCallback.flush();
|
||||
},
|
||||
triggerCallbackPromise : function() {
|
||||
triggerCallbackPromise: function() {
|
||||
$timeout.flush(0);
|
||||
},
|
||||
triggerCallbacks : function() {
|
||||
triggerCallbacks: function() {
|
||||
this.triggerCallbackEvents();
|
||||
this.triggerCallbackPromise();
|
||||
},
|
||||
triggerReflow : function() {
|
||||
triggerReflow: function() {
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
@@ -801,12 +814,13 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
};
|
||||
|
||||
angular.forEach(
|
||||
['enter','leave','move','addClass','removeClass','setClass'], function(method) {
|
||||
['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
|
||||
animate[method] = function() {
|
||||
animate.queue.push({
|
||||
event : method,
|
||||
element : arguments[0],
|
||||
args : arguments
|
||||
event: method,
|
||||
element: arguments[0],
|
||||
options: arguments[arguments.length - 1],
|
||||
args: arguments
|
||||
});
|
||||
return $delegate[method].apply($delegate, arguments);
|
||||
};
|
||||
@@ -873,13 +887,13 @@ angular.mock.dump = function(object) {
|
||||
function serializeScope(scope, offset) {
|
||||
offset = offset || ' ';
|
||||
var log = [offset + 'Scope(' + scope.$id + '): {'];
|
||||
for ( var key in scope ) {
|
||||
for (var key in scope) {
|
||||
if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
|
||||
log.push(' ' + key + ': ' + angular.toJson(scope[key]));
|
||||
}
|
||||
}
|
||||
var child = scope.$$childHead;
|
||||
while(child) {
|
||||
while (child) {
|
||||
log.push(serializeScope(child, offset + ' '));
|
||||
child = child.$$nextSibling;
|
||||
}
|
||||
@@ -991,6 +1005,11 @@ angular.mock.dump = function(object) {
|
||||
* First we create the controller under test:
|
||||
*
|
||||
```js
|
||||
// The module code
|
||||
angular
|
||||
.module('MyApp', [])
|
||||
.controller('MyController', MyController);
|
||||
|
||||
// The controller code
|
||||
function MyController($scope, $http) {
|
||||
var authToken;
|
||||
@@ -1020,6 +1039,9 @@ angular.mock.dump = function(object) {
|
||||
describe('MyController', function() {
|
||||
var $httpBackend, $rootScope, createController, authRequestHandler;
|
||||
|
||||
// Set up the module
|
||||
beforeEach(module('MyApp'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
// Set up the mock http service responses
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
@@ -1368,7 +1390,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||
$httpBackend.expect = function(method, url, data, headers) {
|
||||
var expectation = new MockHttpExpectation(method, url, data, headers),
|
||||
chain = {
|
||||
respond: function (status, data, headers, statusText) {
|
||||
respond: function(status, data, headers, statusText) {
|
||||
expectation.response = createResponse(status, data, headers, statusText);
|
||||
return chain;
|
||||
}
|
||||
@@ -1618,7 +1640,9 @@ function MockHttpExpectation(method, url, data, headers) {
|
||||
if (angular.isUndefined(data)) return true;
|
||||
if (data && angular.isFunction(data.test)) return data.test(d);
|
||||
if (data && angular.isFunction(data)) return data(d);
|
||||
if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
|
||||
if (data && !angular.isString(data)) {
|
||||
return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
|
||||
}
|
||||
return data == d;
|
||||
};
|
||||
|
||||
@@ -1691,7 +1715,7 @@ function MockXhr() {
|
||||
* that adds a "flush" and "verifyNoPendingTasks" methods.
|
||||
*/
|
||||
|
||||
angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function ($delegate, $browser) {
|
||||
angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -1745,12 +1769,12 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
|
||||
rafFn.supported = $delegate.supported;
|
||||
|
||||
rafFn.flush = function() {
|
||||
if(queue.length === 0) {
|
||||
if (queue.length === 0) {
|
||||
throw new Error('No rAF callbacks present');
|
||||
}
|
||||
|
||||
var length = queue.length;
|
||||
for(var i=0;i<length;i++) {
|
||||
for (var i = 0; i < length; i++) {
|
||||
queue[i]();
|
||||
}
|
||||
|
||||
@@ -1810,6 +1834,7 @@ angular.module('ngMock', ['ng']).provider({
|
||||
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
||||
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
||||
$provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
|
||||
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
|
||||
}]);
|
||||
|
||||
/**
|
||||
@@ -2018,7 +2043,93 @@ angular.mock.e2e.$httpBackendDecorator =
|
||||
['$rootScope', '$delegate', '$browser', createHttpBackendMock];
|
||||
|
||||
|
||||
if(window.jasmine || window.mocha) {
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name $rootScope.Scope
|
||||
* @module ngMock
|
||||
* @description
|
||||
* {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
|
||||
* methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
|
||||
* `ngMock` module is loaded.
|
||||
*
|
||||
* In addition to all the regular `Scope` methods, the following helper methods are available:
|
||||
*/
|
||||
angular.mock.$RootScopeDecorator = function($delegate) {
|
||||
|
||||
var $rootScopePrototype = Object.getPrototypeOf($delegate);
|
||||
|
||||
$rootScopePrototype.$countChildScopes = countChildScopes;
|
||||
$rootScopePrototype.$countWatchers = countWatchers;
|
||||
|
||||
return $delegate;
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $rootScope.Scope#$countChildScopes
|
||||
* @module ngMock
|
||||
* @description
|
||||
* Counts all the direct and indirect child scopes of the current scope.
|
||||
*
|
||||
* The current scope is excluded from the count. The count includes all isolate child scopes.
|
||||
*
|
||||
* @returns {number} Total number of child scopes.
|
||||
*/
|
||||
function countChildScopes() {
|
||||
// jshint validthis: true
|
||||
var count = 0; // exclude the current scope
|
||||
var pendingChildHeads = [this.$$childHead];
|
||||
var currentScope;
|
||||
|
||||
while (pendingChildHeads.length) {
|
||||
currentScope = pendingChildHeads.shift();
|
||||
|
||||
while (currentScope) {
|
||||
count += 1;
|
||||
pendingChildHeads.push(currentScope.$$childHead);
|
||||
currentScope = currentScope.$$nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $rootScope.Scope#$countWatchers
|
||||
* @module ngMock
|
||||
* @description
|
||||
* Counts all the watchers of direct and indirect child scopes of the current scope.
|
||||
*
|
||||
* The watchers of the current scope are included in the count and so are all the watchers of
|
||||
* isolate child scopes.
|
||||
*
|
||||
* @returns {number} Total number of watchers.
|
||||
*/
|
||||
function countWatchers() {
|
||||
// jshint validthis: true
|
||||
var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
|
||||
var pendingChildHeads = [this.$$childHead];
|
||||
var currentScope;
|
||||
|
||||
while (pendingChildHeads.length) {
|
||||
currentScope = pendingChildHeads.shift();
|
||||
|
||||
while (currentScope) {
|
||||
count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
|
||||
pendingChildHeads.push(currentScope.$$childHead);
|
||||
currentScope = currentScope.$$nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
isSpecRunning = function() {
|
||||
@@ -2226,7 +2337,7 @@ if(window.jasmine || window.mocha) {
|
||||
injector = currentSpec.$injector = angular.injector(modules, strictDi);
|
||||
currentSpec.$injectorStrict = strictDi;
|
||||
}
|
||||
for(var i = 0, ii = blockFns.length; i < ii; i++) {
|
||||
for (var i = 0, ii = blockFns.length; i < ii; i++) {
|
||||
if (currentSpec.$injectorStrict) {
|
||||
// If the injector is strict / strictDi, and the spec wants to inject using automatic
|
||||
// annotation, then annotate the function here.
|
||||
|
||||
162
src/vendor/angular/angular-route.js
vendored
162
src/vendor/angular/angular-route.js
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @license AngularJS v1.3.0-rc.3
|
||||
* @license AngularJS v1.3.4
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
@@ -39,9 +39,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*/
|
||||
function $RouteProvider(){
|
||||
function $RouteProvider() {
|
||||
function inherit(parent, extra) {
|
||||
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
|
||||
return angular.extend(Object.create(parent), extra);
|
||||
}
|
||||
|
||||
var routes = {};
|
||||
@@ -146,27 +146,45 @@ function $RouteProvider(){
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
//copy original route object to preserve params inherited from proto chain
|
||||
var routeCopy = angular.copy(route);
|
||||
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
||||
routeCopy.reloadOnSearch = true;
|
||||
}
|
||||
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
|
||||
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
|
||||
}
|
||||
routes[path] = angular.extend(
|
||||
{reloadOnSearch: true},
|
||||
route,
|
||||
path && pathRegExp(path, route)
|
||||
routeCopy,
|
||||
path && pathRegExp(path, routeCopy)
|
||||
);
|
||||
|
||||
// create redirection for trailing slashes
|
||||
if (path) {
|
||||
var redirectPath = (path[path.length-1] == '/')
|
||||
? path.substr(0, path.length-1)
|
||||
: path +'/';
|
||||
var redirectPath = (path[path.length - 1] == '/')
|
||||
? path.substr(0, path.length - 1)
|
||||
: path + '/';
|
||||
|
||||
routes[redirectPath] = angular.extend(
|
||||
{redirectTo: path},
|
||||
pathRegExp(redirectPath, route)
|
||||
pathRegExp(redirectPath, routeCopy)
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $routeProvider#caseInsensitiveMatch
|
||||
* @description
|
||||
*
|
||||
* A boolean property indicating if routes defined
|
||||
* using this provider should be matched using a case sensitive
|
||||
* algorithm. Defaults to `false`.
|
||||
*/
|
||||
this.caseInsensitiveMatch = false;
|
||||
|
||||
/**
|
||||
* @param path {string} path
|
||||
* @param opts {Object} options
|
||||
@@ -188,7 +206,7 @@ function $RouteProvider(){
|
||||
|
||||
path = path
|
||||
.replace(/([().])/g, '\\$1')
|
||||
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
|
||||
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
|
||||
var optional = option === '?' ? option : null;
|
||||
var star = option === '*' ? option : null;
|
||||
keys.push({ name: key, optional: !!optional });
|
||||
@@ -380,6 +398,10 @@ function $RouteProvider(){
|
||||
* defined in `resolve` route property. Once all of the dependencies are resolved
|
||||
* `$routeChangeSuccess` is fired.
|
||||
*
|
||||
* The route change (and the `$location` change that triggered it) can be prevented
|
||||
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
|
||||
* for more details about event object.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {Route} next Future route information.
|
||||
* @param {Route} current Current route information.
|
||||
@@ -424,6 +446,8 @@ function $RouteProvider(){
|
||||
*/
|
||||
|
||||
var forceReload = false,
|
||||
preparedRoute,
|
||||
preparedRouteIsUpdateOnly,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
@@ -436,11 +460,15 @@ function $RouteProvider(){
|
||||
* {@link ng.$location $location} hasn't changed.
|
||||
*
|
||||
* As a result of that, {@link ngRoute.directive:ngView ngView}
|
||||
* creates new scope, reinstantiates the controller.
|
||||
* creates new scope and reinstantiates the controller.
|
||||
*/
|
||||
reload: function() {
|
||||
forceReload = true;
|
||||
$rootScope.$evalAsync(updateRoute);
|
||||
$rootScope.$evalAsync(function() {
|
||||
// Don't support cancellation of a reload for now...
|
||||
prepareRoute();
|
||||
commitRoute();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -474,7 +502,8 @@ function $RouteProvider(){
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$on('$locationChangeSuccess', updateRoute);
|
||||
$rootScope.$on('$locationChangeStart', prepareRoute);
|
||||
$rootScope.$on('$locationChangeSuccess', commitRoute);
|
||||
|
||||
return $route;
|
||||
|
||||
@@ -512,36 +541,50 @@ function $RouteProvider(){
|
||||
return params;
|
||||
}
|
||||
|
||||
function updateRoute() {
|
||||
var next = parseRoute(),
|
||||
last = $route.current;
|
||||
function prepareRoute($locationEvent) {
|
||||
var lastRoute = $route.current;
|
||||
|
||||
if (next && last && next.$$route === last.$$route
|
||||
&& angular.equals(next.pathParams, last.pathParams)
|
||||
&& !next.reloadOnSearch && !forceReload) {
|
||||
last.params = next.params;
|
||||
angular.copy(last.params, $routeParams);
|
||||
$rootScope.$broadcast('$routeUpdate', last);
|
||||
} else if (next || last) {
|
||||
preparedRoute = parseRoute();
|
||||
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
|
||||
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
|
||||
&& !preparedRoute.reloadOnSearch && !forceReload;
|
||||
|
||||
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
|
||||
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
|
||||
if ($locationEvent) {
|
||||
$locationEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function commitRoute() {
|
||||
var lastRoute = $route.current;
|
||||
var nextRoute = preparedRoute;
|
||||
|
||||
if (preparedRouteIsUpdateOnly) {
|
||||
lastRoute.params = nextRoute.params;
|
||||
angular.copy(lastRoute.params, $routeParams);
|
||||
$rootScope.$broadcast('$routeUpdate', lastRoute);
|
||||
} else if (nextRoute || lastRoute) {
|
||||
forceReload = false;
|
||||
$rootScope.$broadcast('$routeChangeStart', next, last);
|
||||
$route.current = next;
|
||||
if (next) {
|
||||
if (next.redirectTo) {
|
||||
if (angular.isString(next.redirectTo)) {
|
||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
||||
$route.current = nextRoute;
|
||||
if (nextRoute) {
|
||||
if (nextRoute.redirectTo) {
|
||||
if (angular.isString(nextRoute.redirectTo)) {
|
||||
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
|
||||
.replace();
|
||||
} else {
|
||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
||||
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
|
||||
.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$q.when(next).
|
||||
$q.when(nextRoute).
|
||||
then(function() {
|
||||
if (next) {
|
||||
var locals = angular.extend({}, next.resolve),
|
||||
if (nextRoute) {
|
||||
var locals = angular.extend({}, nextRoute.resolve),
|
||||
template, templateUrl;
|
||||
|
||||
angular.forEach(locals, function(value, key) {
|
||||
@@ -549,17 +592,17 @@ function $RouteProvider(){
|
||||
$injector.get(value) : $injector.invoke(value, null, null, key);
|
||||
});
|
||||
|
||||
if (angular.isDefined(template = next.template)) {
|
||||
if (angular.isDefined(template = nextRoute.template)) {
|
||||
if (angular.isFunction(template)) {
|
||||
template = template(next.params);
|
||||
template = template(nextRoute.params);
|
||||
}
|
||||
} else if (angular.isDefined(templateUrl = next.templateUrl)) {
|
||||
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
|
||||
if (angular.isFunction(templateUrl)) {
|
||||
templateUrl = templateUrl(next.params);
|
||||
templateUrl = templateUrl(nextRoute.params);
|
||||
}
|
||||
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
||||
if (angular.isDefined(templateUrl)) {
|
||||
next.loadedTemplateUrl = templateUrl;
|
||||
nextRoute.loadedTemplateUrl = templateUrl;
|
||||
template = $templateRequest(templateUrl);
|
||||
}
|
||||
}
|
||||
@@ -571,16 +614,16 @@ function $RouteProvider(){
|
||||
}).
|
||||
// after route change
|
||||
then(function(locals) {
|
||||
if (next == $route.current) {
|
||||
if (next) {
|
||||
next.locals = locals;
|
||||
angular.copy(next.params, $routeParams);
|
||||
if (nextRoute == $route.current) {
|
||||
if (nextRoute) {
|
||||
nextRoute.locals = locals;
|
||||
angular.copy(nextRoute.params, $routeParams);
|
||||
}
|
||||
$rootScope.$broadcast('$routeChangeSuccess', next, last);
|
||||
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
|
||||
}
|
||||
}, function(error) {
|
||||
if (next == $route.current) {
|
||||
$rootScope.$broadcast('$routeChangeError', next, last, error);
|
||||
if (nextRoute == $route.current) {
|
||||
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -610,11 +653,11 @@ function $RouteProvider(){
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
angular.forEach((string||'').split(':'), function(segment, i) {
|
||||
angular.forEach((string || '').split(':'), function(segment, i) {
|
||||
if (i === 0) {
|
||||
result.push(segment);
|
||||
} else {
|
||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
||||
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
|
||||
var key = segmentMatch[1];
|
||||
result.push(params[key]);
|
||||
result.push(segmentMatch[2] || '');
|
||||
@@ -845,7 +888,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
* Emitted every time the ngView content is reloaded.
|
||||
*/
|
||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
|
||||
function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
function ngViewFactory($route, $anchorScroll, $animate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
@@ -854,7 +897,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
link: function(scope, $element, attr, ctrl, $transclude) {
|
||||
var currentScope,
|
||||
currentElement,
|
||||
previousElement,
|
||||
previousLeaveAnimation,
|
||||
autoScrollExp = attr.autoscroll,
|
||||
onloadExp = attr.onload || '';
|
||||
|
||||
@@ -862,19 +905,20 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
update();
|
||||
|
||||
function cleanupLastView() {
|
||||
if(previousElement) {
|
||||
previousElement.remove();
|
||||
previousElement = null;
|
||||
if (previousLeaveAnimation) {
|
||||
$animate.cancel(previousLeaveAnimation);
|
||||
previousLeaveAnimation = null;
|
||||
}
|
||||
if(currentScope) {
|
||||
|
||||
if (currentScope) {
|
||||
currentScope.$destroy();
|
||||
currentScope = null;
|
||||
}
|
||||
if(currentElement) {
|
||||
$animate.leave(currentElement).then(function() {
|
||||
previousElement = null;
|
||||
if (currentElement) {
|
||||
previousLeaveAnimation = $animate.leave(currentElement);
|
||||
previousLeaveAnimation.then(function() {
|
||||
previousLeaveAnimation = null;
|
||||
});
|
||||
previousElement = currentElement;
|
||||
currentElement = null;
|
||||
}
|
||||
}
|
||||
@@ -894,7 +938,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
|
||||
// function is called before linking the content, which would apply child
|
||||
// directives to non existing elements.
|
||||
var clone = $transclude(newScope, function(clone) {
|
||||
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter () {
|
||||
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
|
||||
if (angular.isDefined(autoScrollExp)
|
||||
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
|
||||
678
src/vendor/angular/angular-sanitize.js
vendored
Normal file
678
src/vendor/angular/angular-sanitize.js
vendored
Normal file
@@ -0,0 +1,678 @@
|
||||
/**
|
||||
* @license AngularJS v1.3.4
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngSanitize
|
||||
* @description
|
||||
*
|
||||
* # ngSanitize
|
||||
*
|
||||
* The `ngSanitize` module provides functionality to sanitize HTML.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngSanitize"></div>
|
||||
*
|
||||
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
|
||||
*/
|
||||
|
||||
/*
|
||||
* HTML Parser By Misko Hevery (misko@hevery.com)
|
||||
* based on: HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $sanitize
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer. The input may also contain SVG markup.
|
||||
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
|
||||
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
|
||||
*
|
||||
* @param {string} html HTML input.
|
||||
* @returns {string} Sanitized HTML.
|
||||
*
|
||||
* @example
|
||||
<example module="sanitizeExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('sanitizeExample', ['ngSanitize'])
|
||||
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
$scope.deliberatelyTrustDangerousSnippet = function() {
|
||||
return $sce.trustAsHtml($scope.snippet);
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Directive</td>
|
||||
<td>How</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-sanitize">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Automatically uses $sanitize</td>
|
||||
<td><pre><div ng-bind-html="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind-html="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-trust">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()">
|
||||
</div></pre>
|
||||
</td>
|
||||
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-default">
|
||||
<td>ng-bind</td>
|
||||
<td>Automatically escapes</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should sanitize the html snippet by default', function() {
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should inline raw snippet if bound to a trusted value', function() {
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('new <b>text</b>');
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
|
||||
'new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
|
||||
"new <b onclick=\"alert(1)\">text</b>");
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $SanitizeProvider() {
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var START_TAG_REGEXP =
|
||||
/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
|
||||
END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
|
||||
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
|
||||
BEGIN_TAG_REGEXP = /^</,
|
||||
BEGING_END_TAGE_REGEXP = /^<\//,
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = makeMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = makeMap("rp,rt"),
|
||||
optionalEndTagElements = angular.extend({},
|
||||
optionalEndTagInlineElements,
|
||||
optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
|
||||
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
||||
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
|
||||
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
||||
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
// SVG Elements
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
||||
var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," +
|
||||
"desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," +
|
||||
"line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," +
|
||||
"stop,svg,switch,text,title,tspan,use");
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var specialElements = makeMap("script,style");
|
||||
|
||||
var validElements = angular.extend({},
|
||||
voidElements,
|
||||
blockElements,
|
||||
inlineElements,
|
||||
optionalEndTagElements,
|
||||
svgElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
|
||||
|
||||
var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
||||
'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
|
||||
'valign,value,vspace,width');
|
||||
|
||||
// SVG attributes (without "id" and "name" attributes)
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
||||
var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
||||
'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,' +
|
||||
'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,' +
|
||||
'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,' +
|
||||
'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,' +
|
||||
'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,' +
|
||||
'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,' +
|
||||
'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,' +
|
||||
'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,' +
|
||||
'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,' +
|
||||
'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,' +
|
||||
'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,' +
|
||||
'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,' +
|
||||
'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,' +
|
||||
'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,' +
|
||||
'zoomAndPan');
|
||||
|
||||
var validAttrs = angular.extend({},
|
||||
uriAttrs,
|
||||
svgAttrs,
|
||||
htmlAttrs);
|
||||
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser(html, handler) {
|
||||
if (typeof html !== 'string') {
|
||||
if (html === null || typeof html === 'undefined') {
|
||||
html = '';
|
||||
} else {
|
||||
html = '' + html;
|
||||
}
|
||||
}
|
||||
var index, chars, match, stack = [], last = html, text;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
while (html) {
|
||||
text = '';
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if (!stack.last() || !specialElements[ stack.last() ]) {
|
||||
|
||||
// Comment
|
||||
if (html.indexOf("<!--") === 0) {
|
||||
// comments containing -- are not allowed unless they terminate the comment
|
||||
index = html.indexOf("--", 4);
|
||||
|
||||
if (index >= 0 && html.lastIndexOf("-->", index) === index) {
|
||||
if (handler.comment) handler.comment(html.substring(4, index));
|
||||
html = html.substring(index + 3);
|
||||
chars = false;
|
||||
}
|
||||
// DOCTYPE
|
||||
} else if (DOCTYPE_REGEXP.test(html)) {
|
||||
match = html.match(DOCTYPE_REGEXP);
|
||||
|
||||
if (match) {
|
||||
html = html.replace(match[0], '');
|
||||
chars = false;
|
||||
}
|
||||
// end tag
|
||||
} else if (BEGING_END_TAGE_REGEXP.test(html)) {
|
||||
match = html.match(END_TAG_REGEXP);
|
||||
|
||||
if (match) {
|
||||
html = html.substring(match[0].length);
|
||||
match[0].replace(END_TAG_REGEXP, parseEndTag);
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if (BEGIN_TAG_REGEXP.test(html)) {
|
||||
match = html.match(START_TAG_REGEXP);
|
||||
|
||||
if (match) {
|
||||
// We only have a valid start-tag if there is a '>'.
|
||||
if (match[4]) {
|
||||
html = html.substring(match[0].length);
|
||||
match[0].replace(START_TAG_REGEXP, parseStartTag);
|
||||
}
|
||||
chars = false;
|
||||
} else {
|
||||
// no ending tag found --- this piece should be encoded as an entity.
|
||||
text += '<';
|
||||
html = html.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (chars) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
text += index < 0 ? html : html.substring(0, index);
|
||||
html = index < 0 ? "" : html.substring(index);
|
||||
|
||||
if (handler.chars) handler.chars(decodeEntities(text));
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
|
||||
function(all, text) {
|
||||
text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
|
||||
|
||||
if (handler.chars) handler.chars(decodeEntities(text));
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag("", stack.last());
|
||||
}
|
||||
|
||||
if (html == last) {
|
||||
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
|
||||
"of html: {0}", html);
|
||||
}
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag(tag, tagName, rest, unary) {
|
||||
tagName = angular.lowercase(tagName);
|
||||
if (blockElements[ tagName ]) {
|
||||
while (stack.last() && inlineElements[ stack.last() ]) {
|
||||
parseEndTag("", stack.last());
|
||||
}
|
||||
}
|
||||
|
||||
if (optionalEndTagElements[ tagName ] && stack.last() == tagName) {
|
||||
parseEndTag("", tagName);
|
||||
}
|
||||
|
||||
unary = voidElements[ tagName ] || !!unary;
|
||||
|
||||
if (!unary)
|
||||
stack.push(tagName);
|
||||
|
||||
var attrs = {};
|
||||
|
||||
rest.replace(ATTR_REGEXP,
|
||||
function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
|
||||
var value = doubleQuotedValue
|
||||
|| singleQuotedValue
|
||||
|| unquotedValue
|
||||
|| '';
|
||||
|
||||
attrs[name] = decodeEntities(value);
|
||||
});
|
||||
if (handler.start) handler.start(tagName, attrs, unary);
|
||||
}
|
||||
|
||||
function parseEndTag(tag, tagName) {
|
||||
var pos = 0, i;
|
||||
tagName = angular.lowercase(tagName);
|
||||
if (tagName)
|
||||
// Find the closest opened tag of the same type
|
||||
for (pos = stack.length - 1; pos >= 0; pos--)
|
||||
if (stack[ pos ] == tagName)
|
||||
break;
|
||||
|
||||
if (pos >= 0) {
|
||||
// Close all the open elements, up the stack
|
||||
for (i = stack.length - 1; i >= pos; i--)
|
||||
if (handler.end) handler.end(stack[ i ]);
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hiddenPre=document.createElement("pre");
|
||||
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
|
||||
/**
|
||||
* decodes all entities into regular string
|
||||
* @param value
|
||||
* @returns {string} A string with decoded entities.
|
||||
*/
|
||||
function decodeEntities(value) {
|
||||
if (!value) { return ''; }
|
||||
|
||||
// Note: IE8 does not preserve spaces at the start/end of innerHTML
|
||||
// so we must capture them and reattach them afterward
|
||||
var parts = spaceRe.exec(value);
|
||||
var spaceBefore = parts[1];
|
||||
var spaceAfter = parts[3];
|
||||
var content = parts[2];
|
||||
if (content) {
|
||||
hiddenPre.innerHTML=content.replace(/</g,"<");
|
||||
// innerText depends on styling as it doesn't display hidden elements.
|
||||
// Therefore, it's better to use textContent not to cause unnecessary
|
||||
// reflows. However, IE<9 don't support textContent so the innerText
|
||||
// fallback is necessary.
|
||||
content = 'textContent' in hiddenPre ?
|
||||
hiddenPre.textContent : hiddenPre.innerText;
|
||||
}
|
||||
return spaceBefore + content + spaceAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns {string} escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
||||
var hi = value.charCodeAt(0);
|
||||
var low = value.charCodeAt(1);
|
||||
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
||||
}).
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.jain('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf, uriValidator) {
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs, unary) {
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && specialElements[tag]) {
|
||||
ignore = tag;
|
||||
}
|
||||
if (!ignore && validElements[tag] === true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key) {
|
||||
var lkey=angular.lowercase(key);
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out(unary ? '/>' : '>');
|
||||
}
|
||||
},
|
||||
end: function(tag) {
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && validElements[tag] === true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignore) {
|
||||
ignore = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars) {
|
||||
if (!ignore) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name linky
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plain email address links.
|
||||
*
|
||||
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
|
||||
* @returns {string} Html-linkified text.
|
||||
*
|
||||
* @usage
|
||||
<span ng-bind-html="linky_expression | linky"></span>
|
||||
*
|
||||
* @example
|
||||
<example module="linkyExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('linkyExample', ['ngSanitize'])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.snippet =
|
||||
'Pretty text with some links:\n'+
|
||||
'http://angularjs.org/,\n'+
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
$scope.snippetWithTarget = 'http://angularjs.org/';
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-target">
|
||||
<td>linky target</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
|
||||
});
|
||||
|
||||
it('should not linkify snippet without the linky filter', function() {
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new http://link.');
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('new http://link.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
|
||||
.toBe('new http://link.');
|
||||
});
|
||||
|
||||
it('should work with the target property', function() {
|
||||
expect(element(by.id('linky-target')).
|
||||
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
|
||||
toBe('http://angularjs.org/');
|
||||
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text, target) {
|
||||
if (!text) return text;
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
var url;
|
||||
var i;
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
html.push('<a ');
|
||||
if (angular.isDefined(target)) {
|
||||
html.push('target="',
|
||||
target,
|
||||
'" ');
|
||||
}
|
||||
html.push('href="',
|
||||
url.replace('"', '"'),
|
||||
'">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
4310
src/vendor/angular/angular.js
vendored
4310
src/vendor/angular/angular.js
vendored
File diff suppressed because it is too large
Load Diff
600
src/vendor/angular/bindonce.js
vendored
600
src/vendor/angular/bindonce.js
vendored
@@ -1,321 +1,321 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
/**
|
||||
* Bindonce - Zero watches binding for AngularJs
|
||||
* @version v0.3.1
|
||||
* @link https://github.com/Pasvaz/bindonce
|
||||
* @author Pasquale Vazzana <pasqualevazzana@gmail.com>
|
||||
* @license MIT License, http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
"use strict";
|
||||
/**
|
||||
* Bindonce - Zero watches binding for AngularJs
|
||||
* @version v0.3.2
|
||||
* @link https://github.com/Pasvaz/bindonce
|
||||
* @author Pasquale Vazzana <pasqualevazzana@gmail.com>
|
||||
* @license MIT License, http://www.opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
var bindonceModule = angular.module('pasvaz.bindonce', []);
|
||||
var bindonceModule = angular.module('pasvaz.bindonce', []);
|
||||
|
||||
bindonceModule.directive('bindonce', function ()
|
||||
{
|
||||
var toBoolean = function (value)
|
||||
{
|
||||
if (value && value.length !== 0)
|
||||
{
|
||||
var v = angular.lowercase("" + value);
|
||||
value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
|
||||
}
|
||||
else
|
||||
{
|
||||
value = false;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
bindonceModule.directive('bindonce', function ()
|
||||
{
|
||||
var toBoolean = function (value)
|
||||
{
|
||||
if (value && value.length !== 0)
|
||||
{
|
||||
var v = angular.lowercase("" + value);
|
||||
value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
|
||||
}
|
||||
else
|
||||
{
|
||||
value = false;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
|
||||
if (isNaN(msie))
|
||||
{
|
||||
msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
|
||||
}
|
||||
var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
|
||||
if (isNaN(msie))
|
||||
{
|
||||
msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
|
||||
}
|
||||
|
||||
var bindonceDirective =
|
||||
{
|
||||
restrict: "AM",
|
||||
controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
|
||||
{
|
||||
var showHideBinder = function (elm, attr, value)
|
||||
{
|
||||
var show = (attr === 'show') ? '' : 'none';
|
||||
var hide = (attr === 'hide') ? '' : 'none';
|
||||
elm.css('display', toBoolean(value) ? show : hide);
|
||||
};
|
||||
var classBinder = function (elm, value)
|
||||
{
|
||||
if (angular.isObject(value) && !angular.isArray(value))
|
||||
{
|
||||
var results = [];
|
||||
angular.forEach(value, function (value, index)
|
||||
{
|
||||
if (value) results.push(index);
|
||||
});
|
||||
value = results;
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
elm.addClass(angular.isArray(value) ? value.join(' ') : value);
|
||||
}
|
||||
};
|
||||
var transclude = function (transcluder, scope)
|
||||
{
|
||||
transcluder.transclude(scope, function (clone)
|
||||
{
|
||||
var parent = transcluder.element.parent();
|
||||
var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
|
||||
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
|
||||
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
|
||||
angular.forEach(clone, function (node)
|
||||
{
|
||||
parentNode.insertBefore(node, afterNextSibling);
|
||||
});
|
||||
});
|
||||
};
|
||||
var bindonceDirective =
|
||||
{
|
||||
restrict: "AM",
|
||||
controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
|
||||
{
|
||||
var showHideBinder = function (elm, attr, value)
|
||||
{
|
||||
var show = (attr === 'show') ? '' : 'none';
|
||||
var hide = (attr === 'hide') ? '' : 'none';
|
||||
elm.css('display', toBoolean(value) ? show : hide);
|
||||
};
|
||||
var classBinder = function (elm, value)
|
||||
{
|
||||
if (angular.isObject(value) && !angular.isArray(value))
|
||||
{
|
||||
var results = [];
|
||||
angular.forEach(value, function (value, index)
|
||||
{
|
||||
if (value) results.push(index);
|
||||
});
|
||||
value = results;
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
elm.addClass(angular.isArray(value) ? value.join(' ') : value);
|
||||
}
|
||||
};
|
||||
var transclude = function (transcluder, scope)
|
||||
{
|
||||
transcluder.transclude(scope, function (clone)
|
||||
{
|
||||
var parent = transcluder.element.parent();
|
||||
var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
|
||||
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
|
||||
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
|
||||
angular.forEach(clone, function (node)
|
||||
{
|
||||
parentNode.insertBefore(node, afterNextSibling);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var ctrl =
|
||||
{
|
||||
watcherRemover: undefined,
|
||||
binders: [],
|
||||
group: $attrs.boName,
|
||||
element: $element,
|
||||
ran: false,
|
||||
var ctrl =
|
||||
{
|
||||
watcherRemover: undefined,
|
||||
binders: [],
|
||||
group: $attrs.boName,
|
||||
element: $element,
|
||||
ran: false,
|
||||
|
||||
addBinder: function (binder)
|
||||
{
|
||||
this.binders.push(binder);
|
||||
addBinder: function (binder)
|
||||
{
|
||||
this.binders.push(binder);
|
||||
|
||||
// In case of late binding (when using the directive bo-name/bo-parent)
|
||||
// it happens only when you use nested bindonce, if the bo-children
|
||||
// are not dom children the linking can follow another order
|
||||
if (this.ran)
|
||||
{
|
||||
this.runBinders();
|
||||
}
|
||||
},
|
||||
// In case of late binding (when using the directive bo-name/bo-parent)
|
||||
// it happens only when you use nested bindonce, if the bo-children
|
||||
// are not dom children the linking can follow another order
|
||||
if (this.ran)
|
||||
{
|
||||
this.runBinders();
|
||||
}
|
||||
},
|
||||
|
||||
setupWatcher: function (bindonceValue)
|
||||
{
|
||||
var that = this;
|
||||
this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
|
||||
{
|
||||
if (newValue === undefined) return;
|
||||
that.removeWatcher();
|
||||
that.checkBindonce(newValue);
|
||||
}, true);
|
||||
},
|
||||
setupWatcher: function (bindonceValue)
|
||||
{
|
||||
var that = this;
|
||||
this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
|
||||
{
|
||||
if (newValue === undefined) return;
|
||||
that.removeWatcher();
|
||||
that.checkBindonce(newValue);
|
||||
}, true);
|
||||
},
|
||||
|
||||
checkBindonce: function (value)
|
||||
{
|
||||
var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
|
||||
// since Angular 1.2 promises are no longer
|
||||
// undefined until they don't get resolved
|
||||
if (typeof promise === 'function')
|
||||
{
|
||||
promise(function ()
|
||||
{
|
||||
that.runBinders();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
that.runBinders();
|
||||
}
|
||||
},
|
||||
checkBindonce: function (value)
|
||||
{
|
||||
var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
|
||||
// since Angular 1.2 promises are no longer
|
||||
// undefined until they don't get resolved
|
||||
if (typeof promise === 'function')
|
||||
{
|
||||
promise(function ()
|
||||
{
|
||||
that.runBinders();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
that.runBinders();
|
||||
}
|
||||
},
|
||||
|
||||
removeWatcher: function ()
|
||||
{
|
||||
if (this.watcherRemover !== undefined)
|
||||
{
|
||||
this.watcherRemover();
|
||||
this.watcherRemover = undefined;
|
||||
}
|
||||
},
|
||||
removeWatcher: function ()
|
||||
{
|
||||
if (this.watcherRemover !== undefined)
|
||||
{
|
||||
this.watcherRemover();
|
||||
this.watcherRemover = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
runBinders: function ()
|
||||
{
|
||||
while (this.binders.length > 0)
|
||||
{
|
||||
var binder = this.binders.shift();
|
||||
if (this.group && this.group != binder.group) continue;
|
||||
var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
|
||||
switch (binder.attr)
|
||||
{
|
||||
case 'boIf':
|
||||
if (toBoolean(value))
|
||||
{
|
||||
transclude(binder, binder.scope.$new());
|
||||
}
|
||||
break;
|
||||
case 'boSwitch':
|
||||
var selectedTranscludes, switchCtrl = binder.controller[0];
|
||||
if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
|
||||
{
|
||||
binder.scope.$eval(binder.attrs.change);
|
||||
angular.forEach(selectedTranscludes, function (selectedTransclude)
|
||||
{
|
||||
transclude(selectedTransclude, binder.scope.$new());
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'boSwitchWhen':
|
||||
var ctrl = binder.controller[0];
|
||||
ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
|
||||
ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
|
||||
break;
|
||||
case 'boSwitchDefault':
|
||||
var ctrl = binder.controller[0];
|
||||
ctrl.cases['?'] = (ctrl.cases['?'] || []);
|
||||
ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
|
||||
break;
|
||||
case 'hide':
|
||||
case 'show':
|
||||
showHideBinder(binder.element, binder.attr, value);
|
||||
break;
|
||||
case 'class':
|
||||
classBinder(binder.element, value);
|
||||
break;
|
||||
case 'text':
|
||||
binder.element.text(value);
|
||||
break;
|
||||
case 'html':
|
||||
binder.element.html(value);
|
||||
break;
|
||||
case 'style':
|
||||
binder.element.css(value);
|
||||
break;
|
||||
case 'src':
|
||||
binder.element.attr(binder.attr, value);
|
||||
if (msie) binder.element.prop('src', value);
|
||||
break;
|
||||
case 'attr':
|
||||
angular.forEach(binder.attrs, function (attrValue, attrKey)
|
||||
{
|
||||
var newAttr, newValue;
|
||||
if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
|
||||
{
|
||||
newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
newValue = binder.scope.$eval(binder.attrs[attrKey]);
|
||||
binder.element.attr(newAttr, newValue);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'href':
|
||||
case 'alt':
|
||||
case 'title':
|
||||
case 'id':
|
||||
case 'value':
|
||||
binder.element.attr(binder.attr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.ran = true;
|
||||
}
|
||||
};
|
||||
runBinders: function ()
|
||||
{
|
||||
while (this.binders.length > 0)
|
||||
{
|
||||
var binder = this.binders.shift();
|
||||
if (this.group && this.group != binder.group) continue;
|
||||
var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
|
||||
switch (binder.attr)
|
||||
{
|
||||
case 'boIf':
|
||||
if (toBoolean(value))
|
||||
{
|
||||
transclude(binder, binder.scope.$new());
|
||||
}
|
||||
break;
|
||||
case 'boSwitch':
|
||||
var selectedTranscludes, switchCtrl = binder.controller[0];
|
||||
if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
|
||||
{
|
||||
binder.scope.$eval(binder.attrs.change);
|
||||
angular.forEach(selectedTranscludes, function (selectedTransclude)
|
||||
{
|
||||
transclude(selectedTransclude, binder.scope.$new());
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'boSwitchWhen':
|
||||
var ctrl = binder.controller[0];
|
||||
ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
|
||||
ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
|
||||
break;
|
||||
case 'boSwitchDefault':
|
||||
var ctrl = binder.controller[0];
|
||||
ctrl.cases['?'] = (ctrl.cases['?'] || []);
|
||||
ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
|
||||
break;
|
||||
case 'hide':
|
||||
case 'show':
|
||||
showHideBinder(binder.element, binder.attr, value);
|
||||
break;
|
||||
case 'class':
|
||||
classBinder(binder.element, value);
|
||||
break;
|
||||
case 'text':
|
||||
binder.element.text(value);
|
||||
break;
|
||||
case 'html':
|
||||
binder.element.html(value);
|
||||
break;
|
||||
case 'style':
|
||||
binder.element.css(value);
|
||||
break;
|
||||
case 'src':
|
||||
binder.element.attr(binder.attr, value);
|
||||
if (msie) binder.element.prop('src', value);
|
||||
break;
|
||||
case 'attr':
|
||||
angular.forEach(binder.attrs, function (attrValue, attrKey)
|
||||
{
|
||||
var newAttr, newValue;
|
||||
if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
|
||||
{
|
||||
newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
newValue = binder.scope.$eval(binder.attrs[attrKey]);
|
||||
binder.element.attr(newAttr, newValue);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'href':
|
||||
case 'alt':
|
||||
case 'title':
|
||||
case 'id':
|
||||
case 'value':
|
||||
binder.element.attr(binder.attr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.ran = true;
|
||||
}
|
||||
};
|
||||
|
||||
angular.extend(this, ctrl);
|
||||
}],
|
||||
angular.extend(this, ctrl);
|
||||
}],
|
||||
|
||||
link: function (scope, elm, attrs, bindonceController)
|
||||
{
|
||||
var value = attrs.bindonce && scope.$eval(attrs.bindonce);
|
||||
if (value !== undefined)
|
||||
{
|
||||
bindonceController.checkBindonce(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindonceController.setupWatcher(attrs.bindonce);
|
||||
elm.bind("$destroy", bindonceController.removeWatcher);
|
||||
}
|
||||
}
|
||||
};
|
||||
link: function (scope, elm, attrs, bindonceController)
|
||||
{
|
||||
var value = attrs.bindonce && scope.$eval(attrs.bindonce);
|
||||
if (value !== undefined)
|
||||
{
|
||||
bindonceController.checkBindonce(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindonceController.setupWatcher(attrs.bindonce);
|
||||
elm.bind("$destroy", bindonceController.removeWatcher);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return bindonceDirective;
|
||||
});
|
||||
return bindonceDirective;
|
||||
});
|
||||
|
||||
angular.forEach(
|
||||
[
|
||||
{ directiveName: 'boShow', attribute: 'show' },
|
||||
{ directiveName: 'boHide', attribute: 'hide' },
|
||||
{ directiveName: 'boClass', attribute: 'class' },
|
||||
{ directiveName: 'boText', attribute: 'text' },
|
||||
{ directiveName: 'boBind', attribute: 'text' },
|
||||
{ directiveName: 'boHtml', attribute: 'html' },
|
||||
{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
|
||||
{ directiveName: 'boSrc', attribute: 'src' },
|
||||
{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
|
||||
{ directiveName: 'boHref', attribute: 'href' },
|
||||
{ directiveName: 'boAlt', attribute: 'alt' },
|
||||
{ directiveName: 'boTitle', attribute: 'title' },
|
||||
{ directiveName: 'boId', attribute: 'id' },
|
||||
{ directiveName: 'boStyle', attribute: 'style' },
|
||||
{ directiveName: 'boValue', attribute: 'value' },
|
||||
{ directiveName: 'boAttr', attribute: 'attr' },
|
||||
angular.forEach(
|
||||
[
|
||||
{ directiveName: 'boShow', attribute: 'show' },
|
||||
{ directiveName: 'boHide', attribute: 'hide' },
|
||||
{ directiveName: 'boClass', attribute: 'class' },
|
||||
{ directiveName: 'boText', attribute: 'text' },
|
||||
{ directiveName: 'boBind', attribute: 'text' },
|
||||
{ directiveName: 'boHtml', attribute: 'html' },
|
||||
{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
|
||||
{ directiveName: 'boSrc', attribute: 'src' },
|
||||
{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
|
||||
{ directiveName: 'boHref', attribute: 'href' },
|
||||
{ directiveName: 'boAlt', attribute: 'alt' },
|
||||
{ directiveName: 'boTitle', attribute: 'title' },
|
||||
{ directiveName: 'boId', attribute: 'id' },
|
||||
{ directiveName: 'boStyle', attribute: 'style' },
|
||||
{ directiveName: 'boValue', attribute: 'value' },
|
||||
{ directiveName: 'boAttr', attribute: 'attr' },
|
||||
|
||||
{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
|
||||
{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
|
||||
{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
|
||||
{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
|
||||
],
|
||||
function (boDirective)
|
||||
{
|
||||
var childPriority = 200;
|
||||
return bindonceModule.directive(boDirective.directiveName, function ()
|
||||
{
|
||||
var bindonceDirective =
|
||||
{
|
||||
priority: boDirective.priority || childPriority,
|
||||
transclude: boDirective.transclude || false,
|
||||
terminal: boDirective.terminal || false,
|
||||
require: ['^bindonce'].concat(boDirective.require || []),
|
||||
controller: boDirective.controller,
|
||||
compile: function (tElement, tAttrs, transclude)
|
||||
{
|
||||
return function (scope, elm, attrs, controllers)
|
||||
{
|
||||
var bindonceController = controllers[0];
|
||||
var name = attrs.boParent;
|
||||
if (name && bindonceController.group !== name)
|
||||
{
|
||||
var element = bindonceController.element.parent();
|
||||
bindonceController = undefined;
|
||||
var parentValue;
|
||||
{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
|
||||
{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
|
||||
{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
|
||||
{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
|
||||
],
|
||||
function (boDirective)
|
||||
{
|
||||
var childPriority = 200;
|
||||
return bindonceModule.directive(boDirective.directiveName, function ()
|
||||
{
|
||||
var bindonceDirective =
|
||||
{
|
||||
priority: boDirective.priority || childPriority,
|
||||
transclude: boDirective.transclude || false,
|
||||
terminal: boDirective.terminal || false,
|
||||
require: ['^bindonce'].concat(boDirective.require || []),
|
||||
controller: boDirective.controller,
|
||||
compile: function (tElement, tAttrs, transclude)
|
||||
{
|
||||
return function (scope, elm, attrs, controllers)
|
||||
{
|
||||
var bindonceController = controllers[0];
|
||||
var name = attrs.boParent;
|
||||
if (name && bindonceController.group !== name)
|
||||
{
|
||||
var element = bindonceController.element.parent();
|
||||
bindonceController = undefined;
|
||||
var parentValue;
|
||||
|
||||
while (element[0].nodeType !== 9 && element.length)
|
||||
{
|
||||
if ((parentValue = element.data('$bindonceController'))
|
||||
&& parentValue.group === name)
|
||||
{
|
||||
bindonceController = parentValue;
|
||||
break;
|
||||
}
|
||||
element = element.parent();
|
||||
}
|
||||
if (!bindonceController)
|
||||
{
|
||||
throw new Error("No bindonce controller: " + name);
|
||||
}
|
||||
}
|
||||
while (element[0].nodeType !== 9 && element.length)
|
||||
{
|
||||
if ((parentValue = element.data('$bindonceController'))
|
||||
&& parentValue.group === name)
|
||||
{
|
||||
bindonceController = parentValue;
|
||||
break;
|
||||
}
|
||||
element = element.parent();
|
||||
}
|
||||
if (!bindonceController)
|
||||
{
|
||||
throw new Error("No bindonce controller: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
bindonceController.addBinder(
|
||||
{
|
||||
element: elm,
|
||||
attr: boDirective.attribute || boDirective.directiveName,
|
||||
attrs: attrs,
|
||||
value: attrs[boDirective.directiveName],
|
||||
interpolate: boDirective.interpolate,
|
||||
group: name,
|
||||
transclude: transclude,
|
||||
controller: controllers.slice(1),
|
||||
scope: scope
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
bindonceController.addBinder(
|
||||
{
|
||||
element: elm,
|
||||
attr: boDirective.attribute || boDirective.directiveName,
|
||||
attrs: attrs,
|
||||
value: attrs[boDirective.directiveName],
|
||||
interpolate: boDirective.interpolate,
|
||||
group: name,
|
||||
transclude: transclude,
|
||||
controller: controllers.slice(1),
|
||||
scope: scope
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return bindonceDirective;
|
||||
});
|
||||
})
|
||||
return bindonceDirective;
|
||||
});
|
||||
})
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user