angular.js/src/Angular.js

1053 lines
30 KiB
JavaScript
Raw Normal View History

'use strict';
2010-04-01 21:10:28 +00:00
////////////////////////////////////
2010-04-01 00:56:16 +00:00
if (typeof document.getAttribute == $undefined)
2010-01-12 00:15:12 +00:00
document.getAttribute = function() {};
2010-01-06 00:36:58 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.lowercase
* @function
*
* @description Converts string to lowercase
* @param {string} string String to be lowercased.
* @returns {string} Lowercased string.
*/
var lowercase = function (string){ return isString(string) ? string.toLowerCase() : string; };
/**
* @workInProgress
* @ngdoc function
2010-11-10 20:02:49 +00:00
* @name angular.uppercase
* @function
*
* @description Converts string to uppercase.
* @param {string} string String to be uppercased.
* @returns {string} Uppercased string.
*/
var uppercase = function (string){ return isString(string) ? string.toUpperCase() : string; };
var manualLowercase = function (s) {
return isString(s)
? s.replace(/[A-Z]/g, function (ch) {return fromCharCode(ch.charCodeAt(0) | 32); })
: s;
};
var manualUppercase = function (s) {
return isString(s)
? s.replace(/[a-z]/g, function (ch) {return fromCharCode(ch.charCodeAt(0) & ~32); })
: s;
};
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
// with correct but slower alternatives.
if ('i' !== 'I'.toLowerCase()) {
lowercase = manualLowercase;
uppercase = manualUppercase;
}
function fromCharCode(code) { return String.fromCharCode(code); }
2011-04-19 23:34:49 +00:00
var _undefined = undefined,
_null = null,
$$scope = '$scope',
$$validate = '$validate',
$angular = 'angular',
$array = 'array',
$boolean = 'boolean',
$console = 'console',
$date = 'date',
$length = 'length',
$name = 'name',
$noop = 'noop',
$null = 'null',
$number = 'number',
$object = 'object',
$string = 'string',
$value = 'value',
$selected = 'selected',
$undefined = 'undefined',
NG_EXCEPTION = 'ng-exception',
NG_VALIDATION_ERROR = 'ng-validation-error',
NOOP = 'noop',
Error = window.Error,
/** holds major version number for IE or NaN for real browsers */
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
push = [].push,
error = window[$console]
? bind(window[$console], window[$console]['error'] || noop)
: noop,
2011-01-26 05:03:37 +00:00
/** @name angular */
angular = window[$angular] || (window[$angular] = {}),
/** @name angular.markup */
2010-07-30 22:19:43 +00:00
angularTextMarkup = extensionMap(angular, 'markup'),
/** @name angular.attrMarkup */
2010-03-25 21:43:05 +00:00
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
/** @name angular.directive */
2010-03-23 21:57:11 +00:00
angularDirective = extensionMap(angular, 'directive'),
/** @name angular.widget */
angularWidget = extensionMap(angular, 'widget', lowercase),
/** @name angular.validator */
2010-03-23 21:57:11 +00:00
angularValidator = extensionMap(angular, 'validator'),
/** @name angular.fileter */
2010-03-23 21:57:11 +00:00
angularFilter = extensionMap(angular, 'filter'),
/** @name angular.formatter */
2010-03-23 21:57:11 +00:00
angularFormatter = extensionMap(angular, 'formatter'),
/** @name angular.service */
2010-04-01 00:56:16 +00:00
angularService = extensionMap(angular, 'service'),
2010-04-21 19:50:05 +00:00
angularCallbacks = extensionMap(angular, 'callbacks'),
nodeName_,
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
uid = ['0', '0', '0'],
DATE_ISOSTRING_LN = 24;
2010-04-01 00:56:16 +00:00
2010-11-25 05:12:52 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.forEach
2010-11-25 05:12:52 +00:00
* @function
*
* @description
* Invokes the `iterator` function once for each item in `obj` collection. The collection can
* either be an object or an array. The `iterator` function is invoked with `iterator(value, key)`,
* where `value` is the value of an object property or an array element and `key` is the object
* property key or array element index. Optionally, `context` can be specified for the iterator
* function.
*
* Note: this function was previously known as `angular.foreach`.
2010-11-25 05:12:52 +00:00
*
<pre>
var values = {name: 'misko', gender: 'male'};
var log = [];
angular.forEach(values, function(value, key){
2010-11-25 05:12:52 +00:00
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender:male']);
</pre>
*
* @param {Object|Array} obj Object to iterate over.
* @param {function()} iterator Iterator function.
* @param {Object} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
2010-11-25 05:12:52 +00:00
*/
function forEach(obj, iterator, context) {
2010-03-23 21:57:11 +00:00
var key;
if (obj) {
2010-04-17 03:10:09 +00:00
if (isFunction(obj)){
2010-04-16 21:01:29 +00:00
for (key in obj) {
if (key != 'prototype' && key != $length && key != $name && obj.hasOwnProperty(key)) {
2010-04-16 21:01:29 +00:00
iterator.call(context, obj[key], key);
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
2010-04-17 03:10:09 +00:00
obj.forEach(iterator, context);
} else if (isObject(obj) && isNumber(obj.length)) {
2010-03-23 21:57:11 +00:00
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
for (key in obj)
iterator.call(context, obj[key], key);
}
}
return obj;
}
function forEachSorted(obj, iterator, context) {
2010-04-01 00:56:16 +00:00
var keys = [];
for (var key in obj) keys.push(key);
keys.sort();
for ( var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
}
function formatError(arg) {
if (arg instanceof Error) {
if (arg.stack) {
arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ?
'Error: ' + arg.message + '\n' + arg.stack : arg.stack;
} else if (arg.sourceURL) {
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
}
}
return arg;
}
/**
* @description
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
* the number string gets longer over time, and it can also overflow, where as the the nextId
* will grow much slower, it is a string, and it will never overflow.
*
* @returns an unique alpha-numeric string
*/
function nextUid() {
var index = uid.length;
var digit;
while(index) {
index--;
digit = uid[index].charCodeAt(0);
if (digit == 57 /*'9'*/) {
uid[index] = 'A';
return uid.join('');
}
if (digit == 90 /*'Z'*/) {
uid[index] = '0';
} else {
uid[index] = String.fromCharCode(digit + 1);
return uid.join('');
}
}
uid.unshift('0');
return uid.join('');
}
2010-11-25 05:03:56 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.extend
* @function
*
* @description
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects.
2010-11-25 05:03:56 +00:00
*
* @param {Object} dst The destination object.
* @param {...Object} src The source object(s).
*/
function extend(dst) {
forEach(arguments, function(obj){
if (obj !== dst) {
forEach(obj, function(value, key){
dst[key] = value;
});
}
2010-03-23 21:57:11 +00:00
});
return dst;
}
2010-11-25 05:03:56 +00:00
2010-07-15 20:13:21 +00:00
function inherit(parent, extra) {
return extend(new (extend(function(){}, {prototype:parent}))(), extra);
2010-09-14 21:22:15 +00:00
}
2010-07-15 20:13:21 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.noop
* @function
*
* @description
* Empty function that performs no operation whatsoever. This function is useful when writing code
* in the functional style.
<pre>
function foo(callback) {
var result = calculateResult();
(callback || angular.noop)(result);
}
</pre>
*/
function noop() {}
2010-11-25 02:55:34 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.identity
* @function
*
* @description
* A function that does nothing except for returning its first argument. This function is useful
* when writing code in the functional style.
*
<pre>
function transformer(transformationFn, value) {
return (transformationFn || identity)(value);
};
</pre>
*/
function identity($) {return $;}
2010-11-25 02:55:34 +00:00
function valueFn(value) {return function(){ return value; };}
function extensionMap(angular, name, transform) {
var extPoint;
return angular[name] || (extPoint = angular[name] = function (name, fn, prop){
name = (transform || identity)(name);
if (isDefined(fn)) {
extPoint[name] = extend(fn, prop || {});
}
return extPoint[name];
});
}
/**
* @workInProgress
* @ngdoc function
* @name angular.isUndefined
* @function
*
* @description
* Checks if a reference is undefined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
function isUndefined(value){ return typeof value == $undefined; }
/**
* @workInProgress
* @ngdoc function
* @name angular.isDefined
* @function
*
* @description
* Checks if a reference is defined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
function isDefined(value){ return typeof value != $undefined; }
/**
* @workInProgress
* @ngdoc function
* @name angular.isObject
* @function
*
* @description
* Checks if a reference is an `Object`. Unlike in JavaScript `null`s are not considered to be
* objects.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Object` but not `null`.
*/
function isObject(value){ return value!=null && typeof value == $object;}
/**
* @workInProgress
* @ngdoc function
* @name angular.isString
* @function
*
* @description
* Checks if a reference is a `String`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
function isString(value){ return typeof value == $string;}
/**
* @workInProgress
* @ngdoc function
* @name angular.isNumber
* @function
*
* @description
* Checks if a reference is a `Number`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
function isNumber(value){ return typeof value == $number;}
/**
* @workInProgress
* @ngdoc function
* @name angular.isDate
* @function
*
* @description
* Checks if value is a date.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Date`.
*/
2010-11-07 06:50:04 +00:00
function isDate(value){ return value instanceof Date; }
/**
* @workInProgress
* @ngdoc function
* @name angular.isArray
* @function
*
* @description
* Checks if a reference is an `Array`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
2010-03-23 04:29:57 +00:00
function isArray(value) { return value instanceof Array; }
/**
* @workInProgress
* @ngdoc function
* @name angular.isFunction
* @function
*
* @description
* Checks if a reference is a `Function`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
function isFunction(value){ return typeof value == 'function';}
/**
* Checks if `obj` is a window object.
*
* @private
* @param {*} obj Object to check
* @returns {boolean} True if `obj` is a window obj.
*/
function isWindow(obj) {
return obj && obj.document && obj.location && obj.alert && obj.setInterval;
}
function isBoolean(value) { return typeof value == $boolean; }
function isTextNode(node) { return nodeName_(node) == '#text'; }
function trim(value) {
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
}
function isElement(node) {
return node &&
(node.nodeName // we are a direct element
|| (node.bind && node.find)); // we have a bind and find method part of jQuery API
2010-04-23 00:11:56 +00:00
}
/**
* @param str 'key1,key2,...'
* @returns {object} in the form of {key1:true, key2:true, ...}
*/
function makeMap(str){
var obj = {}, items = str.split(","), i;
for ( i = 0; i < items.length; i++ )
obj[ items[i] ] = true;
return obj;
}
/**
* HTML class which is the only class which can be used in ng:bind to inline HTML for security
* reasons.
*
* @constructor
* @param html raw (unsafe) html
* @param {string=} option If set to 'usafe', get method will return raw (unsafe/unsanitized) html
*/
function HTML(html, option) {
2010-04-23 00:11:56 +00:00
this.html = html;
this.get = lowercase(option) == 'unsafe'
? valueFn(html)
: function htmlSanitize() {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf));
return buf.join('');
};
}
2010-04-08 20:43:40 +00:00
if (msie < 9) {
nodeName_ = function(element) {
element = element.nodeName ? element : element[0];
return (element.scopeName && element.scopeName != 'HTML')
? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
2010-04-21 19:50:05 +00:00
};
} else {
nodeName_ = function(element) {
return element.nodeName ? element.nodeName : element[0].nodeName;
2010-04-21 19:50:05 +00:00
};
}
2010-04-08 20:43:40 +00:00
function isVisible(element) {
2010-04-19 21:36:41 +00:00
var rect = element[0].getBoundingClientRect(),
2010-04-19 21:41:36 +00:00
width = (rect.width || (rect.right||0 - rect.left||0)),
height = (rect.height || (rect.bottom||0 - rect.top||0));
2010-04-19 21:36:41 +00:00
return width>0 && height>0;
2010-04-08 20:43:40 +00:00
}
function map(obj, iterator, context) {
var results = [];
forEach(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
2010-04-04 00:04:36 +00:00
}
2010-11-25 01:21:37 +00:00
/**
* @ngdoc function
* @name angular.Object.size
* @function
*
* @description
* Determines the number of elements in an array, the number of properties an object has, or
* the length of a string.
2010-11-25 01:21:37 +00:00
*
* Note: This function is used to augment the Object type in Angular expressions. See
* {@link angular.Object} for more information about Angular arrays.
2010-11-25 01:21:37 +00:00
*
* @param {Object|Array|string} obj Object, array, or string to inspect.
* @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
* @returns {number} The size of `obj` or `0` if `obj` is neither an object or an array.
2010-11-25 01:21:37 +00:00
*
* @example
* <doc:example>
* <doc:source>
* <script>
* function SizeCtrl() {
* this.fooStringLength = angular.Object.size('foo');
* }
* </script>
* <div ng:controller="SizeCtrl">
* Number of items in array: {{ [1,2].$size() }}<br/>
* Number of items in object: {{ {a:1, b:2, c:3}.$size() }}<br/>
* String length: {{fooStringLength}}
* </div>
* </doc:source>
* <doc:scenario>
* it('should print correct sizes for an array and an object', function() {
* expect(binding('[1,2].$size()')).toBe('2');
* expect(binding('{a:1, b:2, c:3}.$size()')).toBe('3');
* expect(binding('fooStringLength')).toBe('3');
* });
* </doc:scenario>
* </doc:example>
2010-11-25 01:21:37 +00:00
*/
function size(obj, ownPropsOnly) {
var size = 0, key;
if (isArray(obj) || isString(obj)) {
return obj.length;
} else if (isObject(obj)){
for (key in obj)
if (!ownPropsOnly || obj.hasOwnProperty(key))
size++;
}
return size;
}
function includes(array, obj) {
for ( var i = 0; i < array.length; i++) {
if (obj === array[i]) return true;
}
return false;
}
2010-03-20 05:18:39 +00:00
function indexOf(array, obj) {
for ( var i = 0; i < array.length; i++) {
if (obj === array[i]) return i;
}
return -1;
}
2010-01-12 00:15:12 +00:00
function isLeafNode (node) {
2010-03-23 21:57:11 +00:00
if (node) {
switch (node.nodeName) {
case "OPTION":
case "PRE":
case "TITLE":
return true;
}
2010-01-06 00:36:58 +00:00
}
2010-03-23 21:57:11 +00:00
return false;
2010-01-12 00:15:12 +00:00
}
2010-01-06 00:36:58 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.copy
* @function
*
* @description
* Alias for {@link angular.Object.copy}
*/
2010-09-21 16:55:09 +00:00
/**
2010-11-25 01:32:04 +00:00
* @ngdoc function
* @name angular.Object.copy
* @function
2010-09-21 16:55:09 +00:00
*
2010-11-25 01:32:04 +00:00
* @description
* Creates a deep copy of `source`, which should be an object or an array.
2010-09-21 16:55:09 +00:00
*
* * If no destination is supplied, a copy of the object or array is created.
* * If a destination is provided, all of its elements (for array) or properties (for objects)
* are deleted and then all elements/properties from the source are copied to it.
* * If `source` is not an object or array, `source` is returned.
*
* Note: this function is used to augment the Object type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
2010-09-21 16:55:09 +00:00
*
* @param {*} source The source that will be used to make a copy.
* Can be any type, including primitives, `null`, and `undefined`.
* @param {(Object|Array)=} destination Destination into which the source is copied. If
2011-04-07 19:48:14 +00:00
* provided, must be of the same type as `source`.
* @returns {*} The copy or updated `destination`, if `destination` was specified.
2010-11-25 01:32:04 +00:00
*
* @example
* <doc:example>
* <doc:source>
Salutation: <input type="text" name="master.salutation" value="Hello" /><br/>
Name: <input type="text" name="master.name" value="world"/><br/>
<button ng:click="form = master.$copy()">copy</button>
<hr/>
The master object is <span ng:hide="master.$equals(form)">NOT</span> equal to the form object.
<pre>master={{master}}</pre>
<pre>form={{form}}</pre>
* </doc:source>
* <doc:scenario>
it('should print that initialy the form object is NOT equal to master', function() {
2011-03-03 17:52:35 +00:00
expect(element('.doc-example-live input[name=master.salutation]').val()).toBe('Hello');
expect(element('.doc-example-live input[name=master.name]').val()).toBe('world');
expect(element('.doc-example-live span').css('display')).toBe('inline');
});
it('should make form and master equal when the copy button is clicked', function() {
2011-03-03 17:52:35 +00:00
element('.doc-example-live button').click();
expect(element('.doc-example-live span').css('display')).toBe('none');
});
* </doc:scenario>
* </doc:example>
2010-09-21 16:55:09 +00:00
*/
2010-03-15 21:36:50 +00:00
function copy(source, destination){
if (!destination) {
destination = source;
2010-05-07 19:09:14 +00:00
if (source) {
if (isArray(source)) {
destination = copy(source, []);
2010-11-07 06:50:04 +00:00
} else if (isDate(source)) {
destination = new Date(source.getTime());
2010-05-07 19:09:14 +00:00
} else if (isObject(source)) {
destination = copy(source, {});
2010-05-07 19:09:14 +00:00
}
2010-03-15 21:36:50 +00:00
}
} else {
if (isArray(source)) {
2010-03-15 21:36:50 +00:00
while(destination.length) {
destination.pop();
}
2010-05-31 03:21:40 +00:00
for ( var i = 0; i < source.length; i++) {
destination.push(copy(source[i]));
}
2010-03-15 21:36:50 +00:00
} else {
forEach(destination, function(value, key){
2010-03-15 21:36:50 +00:00
delete destination[key];
});
2010-05-31 03:21:40 +00:00
for ( var key in source) {
destination[key] = copy(source[key]);
}
2010-03-15 21:36:50 +00:00
}
}
return destination;
2010-04-04 00:04:36 +00:00
}
2010-03-15 21:36:50 +00:00
/**
* @ngdoc function
* @name angular.equals
* @function
*
* @description
* Alias for {@link angular.Object.equals}
*/
/**
* @ngdoc function
* @name angular.Object.equals
* @function
*
* @description
* Determines if two objects or two values are equivalent. Supports value types, arrays and
* objects.
*
* Two objects or values are considered equivalent if at least one of the following is true:
*
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
*
* During a property comparision, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
* Note: This function is used to augment the Object type in Angular expressions. See
* {@link angular.Array} for more information about Angular arrays.
2010-11-25 01:21:37 +00:00
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
* @returns {boolean} True if arguments are equal.
*
* @example
* <doc:example>
* <doc:source>
Salutation: <input type="text" name="greeting.salutation" value="Hello" /><br/>
Name: <input type="text" name="greeting.name" value="world"/><br/>
<hr/>
The <code>greeting</code> object is
<span ng:hide="greeting.$equals({salutation:'Hello', name:'world'})">NOT</span> equal to
<code>{salutation:'Hello', name:'world'}</code>.
<pre>greeting={{greeting}}</pre>
* </doc:source>
* <doc:scenario>
it('should print that initialy greeting is equal to the hardcoded value object', function() {
2011-03-03 17:52:35 +00:00
expect(element('.doc-example-live input[name=greeting.salutation]').val()).toBe('Hello');
expect(element('.doc-example-live input[name=greeting.name]').val()).toBe('world');
expect(element('.doc-example-live span').css('display')).toBe('none');
});
it('should say that the objects are not equal when the form is modified', function() {
input('greeting.name').enter('kitty');
2011-03-03 17:52:35 +00:00
expect(element('.doc-example-live span').css('display')).toBe('inline');
});
* </doc:scenario>
* </doc:example>
*/
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
if (t1 == t2 && t1 == 'object') {
if (o1 instanceof Array) {
if ((length = o1.length) == o2.length) {
for(key=0; key<length; key++) {
if (!equals(o1[key], o2[key])) return false;
}
return true;
}
} else {
keySet = {};
for(key in o1) {
if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) {
return false;
}
keySet[key] = true;
}
for(key in o2) {
if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false;
}
return true;
}
}
return false;
}
2010-01-12 00:15:12 +00:00
function setHtml(node, html) {
2010-01-09 23:02:43 +00:00
if (isLeafNode(node)) {
if (msie) {
2010-01-06 00:36:58 +00:00
node.innerText = html;
} else {
node.textContent = html;
}
} else {
node.innerHTML = html;
}
2010-01-12 00:15:12 +00:00
}
2010-01-06 00:36:58 +00:00
2010-04-05 18:46:53 +00:00
function isRenderableElement(element) {
var name = element && element[0] && element[0].nodeName;
return name && name.charAt(0) != '#' &&
!includes(['TR', 'COL', 'COLGROUP', 'TBODY', 'THEAD', 'TFOOT'], name);
}
2010-03-30 22:39:51 +00:00
function elementError(element, type, error) {
var parent;
2010-04-05 18:46:53 +00:00
while (!isRenderableElement(element)) {
parent = element.parent();
if (parent.length) {
element = element.parent();
} else {
return;
}
2010-04-05 18:46:53 +00:00
}
2010-05-30 23:34:59 +00:00
if (element[0]['$NG_ERROR'] !== error) {
element[0]['$NG_ERROR'] = error;
if (error) {
element.addClass(type);
element.attr(type, error.message || error);
2010-05-30 23:34:59 +00:00
} else {
element.removeClass(type);
element.removeAttr(type);
}
2010-03-30 04:36:34 +00:00
}
}
function concat(array1, array2, index) {
return array1.concat(slice.call(array2, index));
}
function sliceArgs(args, startIndex) {
return slice.call(args, startIndex || 0);
}
2010-11-25 06:33:40 +00:00
/**
* @workInProgress
* @ngdoc function
* @name angular.bind
* @function
*
* @description
* Returns a function which calls function `fn` bound to `self`
* (`self` becomes the `this` for `fn`).
*
2010-11-25 06:33:40 +00:00
* Optional `args` can be supplied which are prebound to the function, also known as
* [function currying](http://en.wikipedia.org/wiki/Currying).
*
* @param {Object} self Context which `fn` should be evaluated in.
2010-11-25 06:33:40 +00:00
* @param {function()} fn Function to be bound.
2010-12-08 00:06:31 +00:00
* @param {...*} args Optional arguments to be prebound to the `fn` function call.
2010-11-25 06:33:40 +00:00
* @returns {function()} Function that wraps the `fn` with all the specified bindings.
*/
function bind(self, fn) {
var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
if (isFunction(fn) && !(fn instanceof RegExp)) {
return curryArgs.length
? function() {
return arguments.length
? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
: fn.apply(self, curryArgs);
}
: function() {
return arguments.length
? fn.apply(self, arguments)
: fn.call(self);
};
} else {
// in IE, native methods are not functions and so they can not be bound
// (but they don't need to be)
return fn;
}
2010-01-12 00:15:12 +00:00
}
2010-01-06 00:36:58 +00:00
2010-01-12 00:15:12 +00:00
function toBoolean(value) {
2010-03-30 21:55:04 +00:00
if (value && value.length !== 0) {
var v = lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
2010-03-30 21:55:04 +00:00
} else {
2010-01-06 00:36:58 +00:00
value = false;
2010-03-30 21:55:04 +00:00
}
return value;
2010-01-12 00:15:12 +00:00
}
2010-01-06 00:36:58 +00:00
2010-11-25 03:14:34 +00:00
/** @name angular.compile */
function compile(element) {
return new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget)
.compile(element);
2010-04-01 00:56:16 +00:00
}
/////////////////////////////////////////////////
/**
* Parses an escaped url query string into key-value pairs.
* @returns Object.<(string|boolean)>
*/
function parseKeyValue(/**string*/keyValue) {
2010-04-01 21:10:28 +00:00
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue){
2010-04-01 21:10:28 +00:00
if (keyValue) {
key_value = keyValue.split('=');
2010-07-30 17:56:36 +00:00
key = unescape(key_value[0]);
obj[key] = isDefined(key_value[1]) ? unescape(key_value[1]) : true;
2010-04-01 21:10:28 +00:00
}
});
return obj;
}
2010-04-02 18:10:36 +00:00
function toKeyValue(obj) {
var parts = [];
forEach(obj, function(value, key) {
parts.push(escape(key) + (value === true ? '' : '=' + escape(value)));
2010-04-02 18:10:36 +00:00
});
return parts.length ? parts.join('&') : '';
2010-04-04 00:04:36 +00:00
}
2010-04-02 18:10:36 +00:00
/**
* 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, '+');
}
/**
* 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), '+');
}
/**
* @ngdoc directive
* @name angular.directive.ng:autobind
* @element script
*
* @TODO ng:autobind is not a directive!! it should be documented as bootstrap parameter in a
* separate bootstrap section.
* @TODO rename to ng:autobind to ng:autoboot
*
* @description
* Technically, ng:autobind is not a directive; it is an Angular bootstrap parameter that can act
* as a directive. It must exist in the script used to boot Angular and can be used only one time.
* For details on bootstrapping Angular, see {@link guide/dev_guide.bootstrap Initializing Angular}
* in the Angular Developer Guide.
*
* `ng:autobind` with no parameters tells Angular to compile and manage the whole page.
*
* `ng:autobind="[root element ID]"` tells Angular to compile and manage part of the document,
2011-06-06 22:00:54 +00:00
* starting at "root element ID".
2011-01-19 20:16:38 +00:00
*
*/
function angularInit(config, document){
var autobind = config.autobind;
if (autobind) {
var element = isString(autobind) ? document.getElementById(autobind) : document,
scope = compile(element)(createScope()),
$browser = scope.$service('$browser');
if (config.css)
$browser.addCss(config.base_url + config.css);
else if(msie<8)
$browser.addJs(config.ie_compat, config.ie_compat_id);
scope.$apply();
2010-04-01 21:10:28 +00:00
}
}
2010-04-07 17:17:15 +00:00
function angularJsConfig(document, config) {
bindJQuery();
var scripts = document.getElementsByTagName("script"),
2010-04-07 17:17:15 +00:00
match;
config = extend({
ie_compat_id: 'ng-ie-compat'
}, config);
2010-04-07 17:17:15 +00:00
for(var j = 0; j < scripts.length; j++) {
match = (scripts[j].src || "").match(rngScript);
2010-04-07 17:17:15 +00:00
if (match) {
config.base_url = match[1];
config.ie_compat = match[1] + 'angular-ie-compat' + (match[2] || '') + '.js';
extend(config, parseKeyValue(match[6]));
eachAttribute(jqLite(scripts[j]), function(value, name){
if (/^ng:/.exec(name)) {
name = name.substring(3).replace(/-/g, '_');
value = value || true;
config[name] = value;
}
});
2010-04-07 17:17:15 +00:00
}
}
return config;
2010-04-07 17:17:15 +00:00
}
function bindJQuery(){
// bind to jQuery if present;
jQuery = window.jQuery;
// reset to jQuery or default to us.
if (jQuery) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope
});
} else {
jqLite = jqLiteWrap;
}
angular.element = jqLite;
}
/**
* throw error of the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
var error = new Error("Argument '" + (name||'?') + "' is " +
(reason || "required"));
throw error;
}
2011-03-29 06:15:28 +00:00
}
function assertArgFn(arg, name) {
assertArg(isFunction(arg), name, 'not a function, got ' +
(typeof arg == 'object' ? arg.constructor.name : typeof arg));
2011-03-29 06:15:28 +00:00
}
/**
* @ngdoc property
* @name angular.version
* @description
* Object which contains information about the current AngularJS version. The object has following
* properties:
*
* - `full` `{string}` full version string, e.g. "0.9.18"
* - `major` `{number}` major version number, e.g. 0
* - `minor` `{number}` minor version number, e.g. 9
* - `dot` `{number}` dot version number, e.g. 18
* - `codeName` `{string}` code name of the release, e.g. "jiggling-armfat"
*/
var version = {
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's
major: "NG_VERSION_MAJOR", // compile task
minor: "NG_VERSION_MINOR",
dot: "NG_VERSION_DOT",
codeName: '"NG_VERSION_CODENAME"'
};