1 /******/ (function(modules) { // webpackBootstrap
2 /******/ // The module cache
3 /******/ var installedModules = {};
5 /******/ // The require function
6 /******/ function __webpack_require__(moduleId) {
8 /******/ // Check if module is in cache
9 /******/ if(installedModules[moduleId])
10 /******/ return installedModules[moduleId].exports;
12 /******/ // Create a new module (and put it into the cache)
13 /******/ var module = installedModules[moduleId] = {
15 /******/ id: moduleId,
16 /******/ loaded: false
19 /******/ // Execute the module function
20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 /******/ // Flag the module as loaded
23 /******/ module.loaded = true;
25 /******/ // Return the exports of the module
26 /******/ return module.exports;
30 /******/ // expose the modules object (__webpack_modules__)
31 /******/ __webpack_require__.m = modules;
33 /******/ // expose the module cache
34 /******/ __webpack_require__.c = installedModules;
36 /******/ // __webpack_public_path__
37 /******/ __webpack_require__.p = "";
39 /******/ // Load entry module and return exports
40 /******/ return __webpack_require__(0);
42 /************************************************************************/
45 /***/ function(module, exports, __webpack_require__) {
47 __webpack_require__(1);
48 __webpack_require__(3);
49 __webpack_require__(4);
50 __webpack_require__(6);
51 __webpack_require__(8);
52 __webpack_require__(9);
53 __webpack_require__(10);
54 __webpack_require__(11);
55 __webpack_require__(12);
56 __webpack_require__(13);
57 __webpack_require__(14);
58 __webpack_require__(15);
59 __webpack_require__(16);
60 __webpack_require__(17);
61 __webpack_require__(18);
62 __webpack_require__(19);
63 __webpack_require__(20);
64 __webpack_require__(21);
65 __webpack_require__(22);
67 __webpack_require__(23);
68 __webpack_require__(24);
70 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
75 /***/ function(module, exports, __webpack_require__) {
77 __webpack_require__(2);
78 module.exports = angular;
83 /***/ function(module, exports) {
86 * @license AngularJS v1.4.8
87 * (c) 2010-2015 Google, Inc. http://angularjs.org
90 (function(window, document, undefined) {'use strict';
95 * This object provides a utility for producing rich Error messages within
96 * Angular. It can be called as follows:
98 * var exampleMinErr = minErr('example');
99 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
101 * The above creates an instance of minErr in the example namespace. The
102 * resulting error will have a namespaced error code of example.one. The
103 * resulting error will replace {0} with the value of foo, and {1} with the
104 * value of bar. The object is not restricted in the number of arguments it can
107 * If fewer arguments are specified than necessary for interpolation, the extra
108 * interpolation markers will be preserved in the final string.
110 * Since data will be parsed statically during a build step, some restrictions
111 * are applied with respect to how minErr instances are created and called.
112 * Instances should have names of the form namespaceMinErr for a minErr created
113 * using minErr('namespace') . Error codes, namespaces and template strings
114 * should all be static strings, not variables or general expressions.
116 * @param {string} module The namespace to use for the new minErr instance.
117 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
118 * error from returned function, for cases when a particular type of error is useful.
119 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
122 function minErr(module, ErrorConstructor) {
123 ErrorConstructor = ErrorConstructor || Error;
125 var SKIP_INDEXES = 2;
127 var templateArgs = arguments,
128 code = templateArgs[0],
129 message = '[' + (module ? module + ':' : '') + code + '] ',
130 template = templateArgs[1],
133 message += template.replace(/\{\d+\}/g, function(match) {
134 var index = +match.slice(1, -1),
135 shiftedIndex = index + SKIP_INDEXES;
137 if (shiftedIndex < templateArgs.length) {
138 return toDebugString(templateArgs[shiftedIndex]);
144 message += '\nhttp://errors.angularjs.org/1.4.8/' +
145 (module ? module + '/' : '') + code;
147 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
148 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
149 encodeURIComponent(toDebugString(templateArgs[i]));
152 return new ErrorConstructor(message);
156 /* We need to tell jshint what variables are being exported */
157 /* global angular: true,
168 REGEX_STRING_REGEXP: true,
169 VALIDITY_STATE_PROPERTY: true,
173 manualLowercase: true,
174 manualUppercase: true,
207 escapeForRegexp: true,
220 toJsonReplacer: true,
223 convertTimezoneToLocal: true,
224 timezoneToOffset: true,
226 tryDecodeURIComponent: true,
229 encodeUriSegment: true,
230 encodeUriQuery: true,
233 getTestability: true,
238 assertNotHasOwnProperty: true,
241 hasOwnProperty: true,
244 NODE_TYPE_ELEMENT: true,
245 NODE_TYPE_ATTRIBUTE: true,
246 NODE_TYPE_TEXT: true,
247 NODE_TYPE_COMMENT: true,
248 NODE_TYPE_DOCUMENT: true,
249 NODE_TYPE_DOCUMENT_FRAGMENT: true,
252 ////////////////////////////////////
261 * The ng module is loaded by default when an AngularJS application is started. The module itself
262 * contains the essential components for an AngularJS application to function. The table below
263 * lists a high level breakdown of each of the services/factories, filters, directives and testing
264 * components available within this core module.
266 * <div doc-module-components="ng"></div>
269 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
271 // The name of a form control's ValidityState property.
272 // This is used so that it's possible for internal tests to create mock ValidityStates.
273 var VALIDITY_STATE_PROPERTY = 'validity';
277 * @name angular.lowercase
281 * @description Converts the specified string to lowercase.
282 * @param {string} string String to be converted to lowercase.
283 * @returns {string} Lowercased string.
285 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
286 var hasOwnProperty = Object.prototype.hasOwnProperty;
290 * @name angular.uppercase
294 * @description Converts the specified string to uppercase.
295 * @param {string} string String to be converted to uppercase.
296 * @returns {string} Uppercased string.
298 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
301 var manualLowercase = function(s) {
302 /* jshint bitwise: false */
304 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
307 var manualUppercase = function(s) {
308 /* jshint bitwise: false */
310 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
315 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
316 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
317 // with correct but slower alternatives.
318 if ('i' !== 'I'.toLowerCase()) {
319 lowercase = manualLowercase;
320 uppercase = manualUppercase;
325 msie, // holds major version number for IE, or NaN if UA is not IE.
326 jqLite, // delay binding since jQuery could be loaded after us.
327 jQuery, // delay binding
331 toString = Object.prototype.toString,
332 getPrototypeOf = Object.getPrototypeOf,
333 ngMinErr = minErr('ng'),
336 angular = window.angular || (window.angular = {}),
341 * documentMode is an IE-only property
342 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
344 msie = document.documentMode;
350 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
353 function isArrayLike(obj) {
355 // `null`, `undefined` and `window` are not array-like
356 if (obj == null || isWindow(obj)) return false;
358 // arrays, strings and jQuery/jqLite objects are array like
359 // * jqLite is either the jQuery or jqLite constructor function
360 // * we have to check the existance of jqLite first as this method is called
361 // via the forEach method when constructing the jqLite object in the first place
362 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
364 // Support: iOS 8.2 (not reproducible in simulator)
365 // "length" in obj used to prevent JIT error (gh-11508)
366 var length = "length" in Object(obj) && obj.length;
368 // NodeList objects (with `item` method) and
369 // other objects with suitable length characteristics are array-like
370 return isNumber(length) &&
371 (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
376 * @name angular.forEach
381 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
382 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
383 * is the value of an object property or an array element, `key` is the object property key or
384 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
386 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
387 * using the `hasOwnProperty` method.
390 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
391 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
392 * return the value provided.
395 var values = {name: 'misko', gender: 'male'};
397 angular.forEach(values, function(value, key) {
398 this.push(key + ': ' + value);
400 expect(log).toEqual(['name: misko', 'gender: male']);
403 * @param {Object|Array} obj Object to iterate over.
404 * @param {Function} iterator Iterator function.
405 * @param {Object=} context Object to become context (`this`) for the iterator function.
406 * @returns {Object|Array} Reference to `obj`.
409 function forEach(obj, iterator, context) {
412 if (isFunction(obj)) {
414 // Need to check if hasOwnProperty exists,
415 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
416 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
417 iterator.call(context, obj[key], key, obj);
420 } else if (isArray(obj) || isArrayLike(obj)) {
421 var isPrimitive = typeof obj !== 'object';
422 for (key = 0, length = obj.length; key < length; key++) {
423 if (isPrimitive || key in obj) {
424 iterator.call(context, obj[key], key, obj);
427 } else if (obj.forEach && obj.forEach !== forEach) {
428 obj.forEach(iterator, context, obj);
429 } else if (isBlankObject(obj)) {
430 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
432 iterator.call(context, obj[key], key, obj);
434 } else if (typeof obj.hasOwnProperty === 'function') {
435 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
437 if (obj.hasOwnProperty(key)) {
438 iterator.call(context, obj[key], key, obj);
442 // Slow path for objects which do not have a method `hasOwnProperty`
444 if (hasOwnProperty.call(obj, key)) {
445 iterator.call(context, obj[key], key, obj);
453 function forEachSorted(obj, iterator, context) {
454 var keys = Object.keys(obj).sort();
455 for (var i = 0; i < keys.length; i++) {
456 iterator.call(context, obj[keys[i]], keys[i]);
463 * when using forEach the params are value, key, but it is often useful to have key, value.
464 * @param {function(string, *)} iteratorFn
465 * @returns {function(*, string)}
467 function reverseParams(iteratorFn) {
468 return function(value, key) { iteratorFn(key, value); };
472 * A consistent way of creating unique IDs in angular.
474 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
475 * we hit number precision issues in JavaScript.
477 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
479 * @returns {number} an unique alpha-numeric string
487 * Set or clear the hashkey for an object.
489 * @param h the hashkey (!truthy to delete the hashkey)
491 function setHashKey(obj, h) {
495 delete obj.$$hashKey;
500 function baseExtend(dst, objs, deep) {
501 var h = dst.$$hashKey;
503 for (var i = 0, ii = objs.length; i < ii; ++i) {
505 if (!isObject(obj) && !isFunction(obj)) continue;
506 var keys = Object.keys(obj);
507 for (var j = 0, jj = keys.length; j < jj; j++) {
511 if (deep && isObject(src)) {
513 dst[key] = new Date(src.valueOf());
514 } else if (isRegExp(src)) {
515 dst[key] = new RegExp(src);
516 } else if (src.nodeName) {
517 dst[key] = src.cloneNode(true);
518 } else if (isElement(src)) {
519 dst[key] = src.clone();
521 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
522 baseExtend(dst[key], [src], true);
536 * @name angular.extend
541 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
542 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
543 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
545 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
546 * {@link angular.merge} for this.
548 * @param {Object} dst Destination object.
549 * @param {...Object} src Source object(s).
550 * @returns {Object} Reference to `dst`.
552 function extend(dst) {
553 return baseExtend(dst, slice.call(arguments, 1), false);
559 * @name angular.merge
564 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
565 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
566 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
568 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
569 * objects, performing a deep copy.
571 * @param {Object} dst Destination object.
572 * @param {...Object} src Source object(s).
573 * @returns {Object} Reference to `dst`.
575 function merge(dst) {
576 return baseExtend(dst, slice.call(arguments, 1), true);
581 function toInt(str) {
582 return parseInt(str, 10);
586 function inherit(parent, extra) {
587 return extend(Object.create(parent), extra);
597 * A function that performs no operations. This function can be useful when writing code in the
600 function foo(callback) {
601 var result = calculateResult();
602 (callback || angular.noop)(result);
612 * @name angular.identity
617 * A function that returns its first argument. This function is useful when writing code in the
621 function transformer(transformationFn, value) {
622 return (transformationFn || angular.identity)(value);
625 * @param {*} value to be returned.
626 * @returns {*} the value passed in.
628 function identity($) {return $;}
629 identity.$inject = [];
632 function valueFn(value) {return function() {return value;};}
634 function hasCustomToString(obj) {
635 return isFunction(obj.toString) && obj.toString !== toString;
641 * @name angular.isUndefined
646 * Determines if a reference is undefined.
648 * @param {*} value Reference to check.
649 * @returns {boolean} True if `value` is undefined.
651 function isUndefined(value) {return typeof value === 'undefined';}
656 * @name angular.isDefined
661 * Determines if a reference is defined.
663 * @param {*} value Reference to check.
664 * @returns {boolean} True if `value` is defined.
666 function isDefined(value) {return typeof value !== 'undefined';}
671 * @name angular.isObject
676 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
677 * considered to be objects. Note that JavaScript arrays are objects.
679 * @param {*} value Reference to check.
680 * @returns {boolean} True if `value` is an `Object` but not `null`.
682 function isObject(value) {
683 // http://jsperf.com/isobject4
684 return value !== null && typeof value === 'object';
689 * Determine if a value is an object with a null prototype
691 * @returns {boolean} True if `value` is an `Object` with a null prototype
693 function isBlankObject(value) {
694 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
700 * @name angular.isString
705 * Determines if a reference is a `String`.
707 * @param {*} value Reference to check.
708 * @returns {boolean} True if `value` is a `String`.
710 function isString(value) {return typeof value === 'string';}
715 * @name angular.isNumber
720 * Determines if a reference is a `Number`.
722 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
724 * If you wish to exclude these then you can use the native
725 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
728 * @param {*} value Reference to check.
729 * @returns {boolean} True if `value` is a `Number`.
731 function isNumber(value) {return typeof value === 'number';}
736 * @name angular.isDate
741 * Determines if a value is a date.
743 * @param {*} value Reference to check.
744 * @returns {boolean} True if `value` is a `Date`.
746 function isDate(value) {
747 return toString.call(value) === '[object Date]';
753 * @name angular.isArray
758 * Determines if a reference is an `Array`.
760 * @param {*} value Reference to check.
761 * @returns {boolean} True if `value` is an `Array`.
763 var isArray = Array.isArray;
767 * @name angular.isFunction
772 * Determines if a reference is a `Function`.
774 * @param {*} value Reference to check.
775 * @returns {boolean} True if `value` is a `Function`.
777 function isFunction(value) {return typeof value === 'function';}
781 * Determines if a value is a regular expression object.
784 * @param {*} value Reference to check.
785 * @returns {boolean} True if `value` is a `RegExp`.
787 function isRegExp(value) {
788 return toString.call(value) === '[object RegExp]';
793 * Checks if `obj` is a window object.
796 * @param {*} obj Object to check
797 * @returns {boolean} True if `obj` is a window obj.
799 function isWindow(obj) {
800 return obj && obj.window === obj;
804 function isScope(obj) {
805 return obj && obj.$evalAsync && obj.$watch;
809 function isFile(obj) {
810 return toString.call(obj) === '[object File]';
814 function isFormData(obj) {
815 return toString.call(obj) === '[object FormData]';
819 function isBlob(obj) {
820 return toString.call(obj) === '[object Blob]';
824 function isBoolean(value) {
825 return typeof value === 'boolean';
829 function isPromiseLike(obj) {
830 return obj && isFunction(obj.then);
834 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
835 function isTypedArray(value) {
836 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
840 var trim = function(value) {
841 return isString(value) ? value.trim() : value;
845 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
846 // Prereq: s is a string.
847 var escapeForRegexp = function(s) {
848 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
849 replace(/\x08/g, '\\x08');
855 * @name angular.isElement
860 * Determines if a reference is a DOM element (or wrapped jQuery element).
862 * @param {*} value Reference to check.
863 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
865 function isElement(node) {
867 (node.nodeName // we are a direct element
868 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
872 * @param str 'key1,key2,...'
873 * @returns {object} in the form of {key1:true, key2:true, ...}
875 function makeMap(str) {
876 var obj = {}, items = str.split(","), i;
877 for (i = 0; i < items.length; i++) {
878 obj[items[i]] = true;
884 function nodeName_(element) {
885 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
888 function includes(array, obj) {
889 return Array.prototype.indexOf.call(array, obj) != -1;
892 function arrayRemove(array, value) {
893 var index = array.indexOf(value);
895 array.splice(index, 1);
907 * Creates a deep copy of `source`, which should be an object or an array.
909 * * If no destination is supplied, a copy of the object or array is created.
910 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
911 * are deleted and then all elements/properties from the source are copied to it.
912 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
913 * * If `source` is identical to 'destination' an exception will be thrown.
915 * @param {*} source The source that will be used to make a copy.
916 * Can be any type, including primitives, `null`, and `undefined`.
917 * @param {(Object|Array)=} destination Destination into which the source is copied. If
918 * provided, must be of the same type as `source`.
919 * @returns {*} The copy or updated `destination`, if `destination` was specified.
922 <example module="copyExample">
923 <file name="index.html">
924 <div ng-controller="ExampleController">
925 <form novalidate class="simple-form">
926 Name: <input type="text" ng-model="user.name" /><br />
927 E-mail: <input type="email" ng-model="user.email" /><br />
928 Gender: <input type="radio" ng-model="user.gender" value="male" />male
929 <input type="radio" ng-model="user.gender" value="female" />female<br />
930 <button ng-click="reset()">RESET</button>
931 <button ng-click="update(user)">SAVE</button>
933 <pre>form = {{user | json}}</pre>
934 <pre>master = {{master | json}}</pre>
938 angular.module('copyExample', [])
939 .controller('ExampleController', ['$scope', function($scope) {
942 $scope.update = function(user) {
943 // Example with 1 argument
944 $scope.master= angular.copy(user);
947 $scope.reset = function() {
948 // Example with 2 arguments
949 angular.copy($scope.master, $scope.user);
958 function copy(source, destination) {
959 var stackSource = [];
963 if (isTypedArray(destination)) {
964 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
966 if (source === destination) {
967 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
970 // Empty the destination object
971 if (isArray(destination)) {
972 destination.length = 0;
974 forEach(destination, function(value, key) {
975 if (key !== '$$hashKey') {
976 delete destination[key];
981 stackSource.push(source);
982 stackDest.push(destination);
983 return copyRecurse(source, destination);
986 return copyElement(source);
988 function copyRecurse(source, destination) {
989 var h = destination.$$hashKey;
991 if (isArray(source)) {
992 for (var i = 0, ii = source.length; i < ii; i++) {
993 destination.push(copyElement(source[i]));
995 } else if (isBlankObject(source)) {
996 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
997 for (key in source) {
998 destination[key] = copyElement(source[key]);
1000 } else if (source && typeof source.hasOwnProperty === 'function') {
1001 // Slow path, which must rely on hasOwnProperty
1002 for (key in source) {
1003 if (source.hasOwnProperty(key)) {
1004 destination[key] = copyElement(source[key]);
1008 // Slowest path --- hasOwnProperty can't be called as a method
1009 for (key in source) {
1010 if (hasOwnProperty.call(source, key)) {
1011 destination[key] = copyElement(source[key]);
1015 setHashKey(destination, h);
1019 function copyElement(source) {
1021 if (!isObject(source)) {
1025 // Already copied values
1026 var index = stackSource.indexOf(source);
1028 return stackDest[index];
1031 if (isWindow(source) || isScope(source)) {
1032 throw ngMinErr('cpws',
1033 "Can't copy! Making copies of Window or Scope instances is not supported.");
1036 var needsRecurse = false;
1039 if (isArray(source)) {
1041 needsRecurse = true;
1042 } else if (isTypedArray(source)) {
1043 destination = new source.constructor(source);
1044 } else if (isDate(source)) {
1045 destination = new Date(source.getTime());
1046 } else if (isRegExp(source)) {
1047 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1048 destination.lastIndex = source.lastIndex;
1049 } else if (isFunction(source.cloneNode)) {
1050 destination = source.cloneNode(true);
1052 destination = Object.create(getPrototypeOf(source));
1053 needsRecurse = true;
1056 stackSource.push(source);
1057 stackDest.push(destination);
1060 ? copyRecurse(source, destination)
1066 * Creates a shallow copy of an object, an array or a primitive.
1068 * Assumes that there are no proto properties for objects.
1070 function shallowCopy(src, dst) {
1074 for (var i = 0, ii = src.length; i < ii; i++) {
1077 } else if (isObject(src)) {
1080 for (var key in src) {
1081 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1082 dst[key] = src[key];
1093 * @name angular.equals
1098 * Determines if two objects or two values are equivalent. Supports value types, regular
1099 * expressions, arrays and objects.
1101 * Two objects or values are considered equivalent if at least one of the following is true:
1103 * * Both objects or values pass `===` comparison.
1104 * * Both objects or values are of the same type and all of their properties are equal by
1105 * comparing them with `angular.equals`.
1106 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1107 * * Both values represent the same regular expression (In JavaScript,
1108 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1109 * representation matches).
1111 * During a property comparison, properties of `function` type and properties with names
1112 * that begin with `$` are ignored.
1114 * Scope and DOMWindow objects are being compared only by identify (`===`).
1116 * @param {*} o1 Object or value to compare.
1117 * @param {*} o2 Object or value to compare.
1118 * @returns {boolean} True if arguments are equal.
1120 function equals(o1, o2) {
1121 if (o1 === o2) return true;
1122 if (o1 === null || o2 === null) return false;
1123 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1124 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1126 if (t1 == 'object') {
1128 if (!isArray(o2)) return false;
1129 if ((length = o1.length) == o2.length) {
1130 for (key = 0; key < length; key++) {
1131 if (!equals(o1[key], o2[key])) return false;
1135 } else if (isDate(o1)) {
1136 if (!isDate(o2)) return false;
1137 return equals(o1.getTime(), o2.getTime());
1138 } else if (isRegExp(o1)) {
1139 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1141 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1142 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1143 keySet = createMap();
1145 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1146 if (!equals(o1[key], o2[key])) return false;
1150 if (!(key in keySet) &&
1151 key.charAt(0) !== '$' &&
1152 isDefined(o2[key]) &&
1153 !isFunction(o2[key])) return false;
1162 var csp = function() {
1163 if (!isDefined(csp.rules)) {
1166 var ngCspElement = (document.querySelector('[ng-csp]') ||
1167 document.querySelector('[data-ng-csp]'));
1170 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1171 ngCspElement.getAttribute('data-ng-csp');
1173 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1174 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1178 noUnsafeEval: noUnsafeEval(),
1179 noInlineStyle: false
1186 function noUnsafeEval() {
1188 /* jshint -W031, -W054 */
1190 /* jshint +W031, +W054 */
1204 * @param {string=} ngJq the name of the library available under `window`
1205 * to be used for angular.element
1207 * Use this directive to force the angular.element library. This should be
1208 * used to force either jqLite by leaving ng-jq blank or setting the name of
1209 * the jquery variable under window (eg. jQuery).
1211 * Since angular looks for this directive when it is loaded (doesn't wait for the
1212 * DOMContentLoaded event), it must be placed on an element that comes before the script
1213 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1217 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1226 * This example shows how to use a jQuery based library of a different name.
1227 * The library name must be available at the top most 'window'.
1230 <html ng-app ng-jq="jQueryLib">
1236 var jq = function() {
1237 if (isDefined(jq.name_)) return jq.name_;
1239 var i, ii = ngAttrPrefixes.length, prefix, name;
1240 for (i = 0; i < ii; ++i) {
1241 prefix = ngAttrPrefixes[i];
1242 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1243 name = el.getAttribute(prefix + 'jq');
1248 return (jq.name_ = name);
1251 function concat(array1, array2, index) {
1252 return array1.concat(slice.call(array2, index));
1255 function sliceArgs(args, startIndex) {
1256 return slice.call(args, startIndex || 0);
1263 * @name angular.bind
1268 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1269 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1270 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1271 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1273 * @param {Object} self Context which `fn` should be evaluated in.
1274 * @param {function()} fn Function to be bound.
1275 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1276 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1279 function bind(self, fn) {
1280 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1281 if (isFunction(fn) && !(fn instanceof RegExp)) {
1282 return curryArgs.length
1284 return arguments.length
1285 ? fn.apply(self, concat(curryArgs, arguments, 0))
1286 : fn.apply(self, curryArgs);
1289 return arguments.length
1290 ? fn.apply(self, arguments)
1294 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1300 function toJsonReplacer(key, value) {
1303 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1305 } else if (isWindow(value)) {
1307 } else if (value && document === value) {
1309 } else if (isScope(value)) {
1319 * @name angular.toJson
1324 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1325 * stripped since angular uses this notation internally.
1327 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1328 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1329 * If set to an integer, the JSON output will contain that many spaces per indentation.
1330 * @returns {string|undefined} JSON-ified string representing `obj`.
1332 function toJson(obj, pretty) {
1333 if (typeof obj === 'undefined') return undefined;
1334 if (!isNumber(pretty)) {
1335 pretty = pretty ? 2 : null;
1337 return JSON.stringify(obj, toJsonReplacer, pretty);
1343 * @name angular.fromJson
1348 * Deserializes a JSON string.
1350 * @param {string} json JSON string to deserialize.
1351 * @returns {Object|Array|string|number} Deserialized JSON string.
1353 function fromJson(json) {
1354 return isString(json)
1360 function timezoneToOffset(timezone, fallback) {
1361 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1362 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1366 function addDateMinutes(date, minutes) {
1367 date = new Date(date.getTime());
1368 date.setMinutes(date.getMinutes() + minutes);
1373 function convertTimezoneToLocal(date, timezone, reverse) {
1374 reverse = reverse ? -1 : 1;
1375 var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1376 return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1381 * @returns {string} Returns the string representation of the element.
1383 function startingTag(element) {
1384 element = jqLite(element).clone();
1386 // turns out IE does not let you set .html() on elements which
1387 // are not allowed to have children. So we just ignore it.
1390 var elemHtml = jqLite('<div>').append(element).html();
1392 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1394 match(/^(<[^>]+>)/)[1].
1395 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1397 return lowercase(elemHtml);
1403 /////////////////////////////////////////////////
1406 * Tries to decode the URI component without throwing an exception.
1409 * @param str value potential URI component to check.
1410 * @returns {boolean} True if `value` can be decoded
1411 * with the decodeURIComponent function.
1413 function tryDecodeURIComponent(value) {
1415 return decodeURIComponent(value);
1417 // Ignore any invalid uri component
1423 * Parses an escaped url query string into key-value pairs.
1424 * @returns {Object.<string,boolean|Array>}
1426 function parseKeyValue(/**string*/keyValue) {
1428 forEach((keyValue || "").split('&'), function(keyValue) {
1429 var splitPoint, key, val;
1431 key = keyValue = keyValue.replace(/\+/g,'%20');
1432 splitPoint = keyValue.indexOf('=');
1433 if (splitPoint !== -1) {
1434 key = keyValue.substring(0, splitPoint);
1435 val = keyValue.substring(splitPoint + 1);
1437 key = tryDecodeURIComponent(key);
1438 if (isDefined(key)) {
1439 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1440 if (!hasOwnProperty.call(obj, key)) {
1442 } else if (isArray(obj[key])) {
1445 obj[key] = [obj[key],val];
1453 function toKeyValue(obj) {
1455 forEach(obj, function(value, key) {
1456 if (isArray(value)) {
1457 forEach(value, function(arrayValue) {
1458 parts.push(encodeUriQuery(key, true) +
1459 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1462 parts.push(encodeUriQuery(key, true) +
1463 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1466 return parts.length ? parts.join('&') : '';
1471 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1472 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1475 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1476 * pct-encoded = "%" HEXDIG HEXDIG
1477 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1478 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1479 * / "*" / "+" / "," / ";" / "="
1481 function encodeUriSegment(val) {
1482 return encodeUriQuery(val, true).
1483 replace(/%26/gi, '&').
1484 replace(/%3D/gi, '=').
1485 replace(/%2B/gi, '+');
1490 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1491 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1492 * encoded per http://tools.ietf.org/html/rfc3986:
1493 * query = *( pchar / "/" / "?" )
1494 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1495 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1496 * pct-encoded = "%" HEXDIG HEXDIG
1497 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1498 * / "*" / "+" / "," / ";" / "="
1500 function encodeUriQuery(val, pctEncodeSpaces) {
1501 return encodeURIComponent(val).
1502 replace(/%40/gi, '@').
1503 replace(/%3A/gi, ':').
1504 replace(/%24/g, '$').
1505 replace(/%2C/gi, ',').
1506 replace(/%3B/gi, ';').
1507 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1510 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1512 function getNgAttribute(element, ngAttr) {
1513 var attr, i, ii = ngAttrPrefixes.length;
1514 for (i = 0; i < ii; ++i) {
1515 attr = ngAttrPrefixes[i] + ngAttr;
1516 if (isString(attr = element.getAttribute(attr))) {
1529 * @param {angular.Module} ngApp an optional application
1530 * {@link angular.module module} name to load.
1531 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1532 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1533 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1534 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1535 * tracking down the root of these bugs.
1539 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1540 * designates the **root element** of the application and is typically placed near the root element
1541 * of the page - e.g. on the `<body>` or `<html>` tags.
1543 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1544 * found in the document will be used to define the root element to auto-bootstrap as an
1545 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1546 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1548 * You can specify an **AngularJS module** to be used as the root module for the application. This
1549 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1550 * should contain the application code needed or have dependencies on other modules that will
1551 * contain the code. See {@link angular.module} for more information.
1553 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1554 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1555 * would not be resolved to `3`.
1557 * `ngApp` is the easiest, and most common way to bootstrap an application.
1559 <example module="ngAppDemo">
1560 <file name="index.html">
1561 <div ng-controller="ngAppDemoController">
1562 I can add: {{a}} + {{b}} = {{ a+b }}
1565 <file name="script.js">
1566 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1573 * Using `ngStrictDi`, you would see something like this:
1575 <example ng-app-included="true">
1576 <file name="index.html">
1577 <div ng-app="ngAppStrictDemo" ng-strict-di>
1578 <div ng-controller="GoodController1">
1579 I can add: {{a}} + {{b}} = {{ a+b }}
1581 <p>This renders because the controller does not fail to
1582 instantiate, by using explicit annotation style (see
1583 script.js for details)
1587 <div ng-controller="GoodController2">
1588 Name: <input ng-model="name"><br />
1591 <p>This renders because the controller does not fail to
1592 instantiate, by using explicit annotation style
1593 (see script.js for details)
1597 <div ng-controller="BadController">
1598 I can add: {{a}} + {{b}} = {{ a+b }}
1600 <p>The controller could not be instantiated, due to relying
1601 on automatic function annotations (which are disabled in
1602 strict mode). As such, the content of this section is not
1603 interpolated, and there should be an error in your web console.
1608 <file name="script.js">
1609 angular.module('ngAppStrictDemo', [])
1610 // BadController will fail to instantiate, due to relying on automatic function annotation,
1611 // rather than an explicit annotation
1612 .controller('BadController', function($scope) {
1616 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1617 // due to using explicit annotations using the array style and $inject property, respectively.
1618 .controller('GoodController1', ['$scope', function($scope) {
1622 .controller('GoodController2', GoodController2);
1623 function GoodController2($scope) {
1624 $scope.name = "World";
1626 GoodController2.$inject = ['$scope'];
1628 <file name="style.css">
1629 div[ng-controller] {
1631 -webkit-border-radius: 4px;
1636 div[ng-controller^=Good] {
1637 border-color: #d6e9c6;
1638 background-color: #dff0d8;
1641 div[ng-controller^=Bad] {
1642 border-color: #ebccd1;
1643 background-color: #f2dede;
1650 function angularInit(element, bootstrap) {
1655 // The element `element` has priority over any other element
1656 forEach(ngAttrPrefixes, function(prefix) {
1657 var name = prefix + 'app';
1659 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1660 appElement = element;
1661 module = element.getAttribute(name);
1664 forEach(ngAttrPrefixes, function(prefix) {
1665 var name = prefix + 'app';
1668 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1669 appElement = candidate;
1670 module = candidate.getAttribute(name);
1674 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1675 bootstrap(appElement, module ? [module] : [], config);
1681 * @name angular.bootstrap
1684 * Use this function to manually start up angular application.
1686 * See: {@link guide/bootstrap Bootstrap}
1688 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1689 * They must use {@link ng.directive:ngApp ngApp}.
1691 * Angular will detect if it has been loaded into the browser more than once and only allow the
1692 * first loaded script to be bootstrapped and will report a warning to the browser console for
1693 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1694 * multiple instances of Angular try to work on the DOM.
1700 * <div ng-controller="WelcomeController">
1704 * <script src="angular.js"></script>
1706 * var app = angular.module('demo', [])
1707 * .controller('WelcomeController', function($scope) {
1708 * $scope.greeting = 'Welcome!';
1710 * angular.bootstrap(document, ['demo']);
1716 * @param {DOMElement} element DOM element which is the root of angular application.
1717 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1718 * Each item in the array should be the name of a predefined module or a (DI annotated)
1719 * function that will be invoked by the injector as a `config` block.
1720 * See: {@link angular.module modules}
1721 * @param {Object=} config an object for defining configuration options for the application. The
1722 * following keys are supported:
1724 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1725 * assist in finding bugs which break minified code. Defaults to `false`.
1727 * @returns {auto.$injector} Returns the newly created injector for this app.
1729 function bootstrap(element, modules, config) {
1730 if (!isObject(config)) config = {};
1731 var defaultConfig = {
1734 config = extend(defaultConfig, config);
1735 var doBootstrap = function() {
1736 element = jqLite(element);
1738 if (element.injector()) {
1739 var tag = (element[0] === document) ? 'document' : startingTag(element);
1740 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1743 "App Already Bootstrapped with this Element '{0}'",
1744 tag.replace(/</,'<').replace(/>/,'>'));
1747 modules = modules || [];
1748 modules.unshift(['$provide', function($provide) {
1749 $provide.value('$rootElement', element);
1752 if (config.debugInfoEnabled) {
1753 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1754 modules.push(['$compileProvider', function($compileProvider) {
1755 $compileProvider.debugInfoEnabled(true);
1759 modules.unshift('ng');
1760 var injector = createInjector(modules, config.strictDi);
1761 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1762 function bootstrapApply(scope, element, compile, injector) {
1763 scope.$apply(function() {
1764 element.data('$injector', injector);
1765 compile(element)(scope);
1772 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1773 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1775 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1776 config.debugInfoEnabled = true;
1777 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1780 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1781 return doBootstrap();
1784 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1785 angular.resumeBootstrap = function(extraModules) {
1786 forEach(extraModules, function(module) {
1787 modules.push(module);
1789 return doBootstrap();
1792 if (isFunction(angular.resumeDeferredBootstrap)) {
1793 angular.resumeDeferredBootstrap();
1799 * @name angular.reloadWithDebugInfo
1802 * Use this function to reload the current application with debug information turned on.
1803 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1805 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1807 function reloadWithDebugInfo() {
1808 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1809 window.location.reload();
1813 * @name angular.getTestability
1816 * Get the testability service for the instance of Angular on the given
1818 * @param {DOMElement} element DOM element which is the root of angular application.
1820 function getTestability(rootElement) {
1821 var injector = angular.element(rootElement).injector();
1823 throw ngMinErr('test',
1824 'no injector found for element argument to getTestability');
1826 return injector.get('$$testability');
1829 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1830 function snake_case(name, separator) {
1831 separator = separator || '_';
1832 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1833 return (pos ? separator : '') + letter.toLowerCase();
1837 var bindJQueryFired = false;
1838 var skipDestroyOnNextJQueryCleanData;
1839 function bindJQuery() {
1840 var originalCleanData;
1842 if (bindJQueryFired) {
1846 // bind to jQuery if present;
1848 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1849 !jqName ? undefined : // use jqLite
1850 window[jqName]; // use jQuery specified by `ngJq`
1852 // Use jQuery if it exists with proper functionality, otherwise default to us.
1853 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1854 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1855 // versions. It will not work for sure with jQuery <1.7, though.
1856 if (jQuery && jQuery.fn.on) {
1859 scope: JQLitePrototype.scope,
1860 isolateScope: JQLitePrototype.isolateScope,
1861 controller: JQLitePrototype.controller,
1862 injector: JQLitePrototype.injector,
1863 inheritedData: JQLitePrototype.inheritedData
1866 // All nodes removed from the DOM via various jQuery APIs like .remove()
1867 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1868 // the $destroy event on all removed nodes.
1869 originalCleanData = jQuery.cleanData;
1870 jQuery.cleanData = function(elems) {
1872 if (!skipDestroyOnNextJQueryCleanData) {
1873 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1874 events = jQuery._data(elem, "events");
1875 if (events && events.$destroy) {
1876 jQuery(elem).triggerHandler('$destroy');
1880 skipDestroyOnNextJQueryCleanData = false;
1882 originalCleanData(elems);
1888 angular.element = jqLite;
1890 // Prevent double-proxying.
1891 bindJQueryFired = true;
1895 * throw error if the argument is falsy.
1897 function assertArg(arg, name, reason) {
1899 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1904 function assertArgFn(arg, name, acceptArrayAnnotation) {
1905 if (acceptArrayAnnotation && isArray(arg)) {
1906 arg = arg[arg.length - 1];
1909 assertArg(isFunction(arg), name, 'not a function, got ' +
1910 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1915 * throw error if the name given is hasOwnProperty
1916 * @param {String} name the name to test
1917 * @param {String} context the context in which the name is used, such as module or directive
1919 function assertNotHasOwnProperty(name, context) {
1920 if (name === 'hasOwnProperty') {
1921 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1926 * Return the value accessible from the object by path. Any undefined traversals are ignored
1927 * @param {Object} obj starting object
1928 * @param {String} path path to traverse
1929 * @param {boolean} [bindFnToScope=true]
1930 * @returns {Object} value as accessible by path
1932 //TODO(misko): this function needs to be removed
1933 function getter(obj, path, bindFnToScope) {
1934 if (!path) return obj;
1935 var keys = path.split('.');
1937 var lastInstance = obj;
1938 var len = keys.length;
1940 for (var i = 0; i < len; i++) {
1943 obj = (lastInstance = obj)[key];
1946 if (!bindFnToScope && isFunction(obj)) {
1947 return bind(lastInstance, obj);
1953 * Return the DOM siblings between the first and last node in the given array.
1954 * @param {Array} array like object
1955 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1957 function getBlockNodes(nodes) {
1958 // TODO(perf): update `nodes` instead of creating a new object?
1959 var node = nodes[0];
1960 var endNode = nodes[nodes.length - 1];
1963 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1964 if (blockNodes || nodes[i] !== node) {
1966 blockNodes = jqLite(slice.call(nodes, 0, i));
1968 blockNodes.push(node);
1972 return blockNodes || nodes;
1977 * Creates a new object without a prototype. This object is useful for lookup without having to
1978 * guard against prototypically inherited properties via hasOwnProperty.
1980 * Related micro-benchmarks:
1981 * - http://jsperf.com/object-create2
1982 * - http://jsperf.com/proto-map-lookup/2
1983 * - http://jsperf.com/for-in-vs-object-keys2
1987 function createMap() {
1988 return Object.create(null);
1991 var NODE_TYPE_ELEMENT = 1;
1992 var NODE_TYPE_ATTRIBUTE = 2;
1993 var NODE_TYPE_TEXT = 3;
1994 var NODE_TYPE_COMMENT = 8;
1995 var NODE_TYPE_DOCUMENT = 9;
1996 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
2000 * @name angular.Module
2004 * Interface for configuring angular {@link angular.module modules}.
2007 function setupModuleLoader(window) {
2009 var $injectorMinErr = minErr('$injector');
2010 var ngMinErr = minErr('ng');
2012 function ensure(obj, name, factory) {
2013 return obj[name] || (obj[name] = factory());
2016 var angular = ensure(window, 'angular', Object);
2018 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2019 angular.$$minErr = angular.$$minErr || minErr;
2021 return ensure(angular, 'module', function() {
2022 /** @type {Object.<string, angular.Module>} */
2027 * @name angular.module
2031 * The `angular.module` is a global place for creating, registering and retrieving Angular
2033 * All modules (angular core or 3rd party) that should be available to an application must be
2034 * registered using this mechanism.
2036 * Passing one argument retrieves an existing {@link angular.Module},
2037 * whereas passing more than one argument creates a new {@link angular.Module}
2042 * A module is a collection of services, directives, controllers, filters, and configuration information.
2043 * `angular.module` is used to configure the {@link auto.$injector $injector}.
2046 * // Create a new module
2047 * var myModule = angular.module('myModule', []);
2049 * // register a new service
2050 * myModule.value('appName', 'MyCoolApp');
2052 * // configure existing services inside initialization blocks.
2053 * myModule.config(['$locationProvider', function($locationProvider) {
2054 * // Configure existing providers
2055 * $locationProvider.hashPrefix('!');
2059 * Then you can create an injector and load your modules like this:
2062 * var injector = angular.injector(['ng', 'myModule'])
2065 * However it's more likely that you'll just use
2066 * {@link ng.directive:ngApp ngApp} or
2067 * {@link angular.bootstrap} to simplify this process for you.
2069 * @param {!string} name The name of the module to create or retrieve.
2070 * @param {!Array.<string>=} requires If specified then new module is being created. If
2071 * unspecified then the module is being retrieved for further configuration.
2072 * @param {Function=} configFn Optional configuration function for the module. Same as
2073 * {@link angular.Module#config Module#config()}.
2074 * @returns {module} new module with the {@link angular.Module} api.
2076 return function module(name, requires, configFn) {
2077 var assertNotHasOwnProperty = function(name, context) {
2078 if (name === 'hasOwnProperty') {
2079 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2083 assertNotHasOwnProperty(name, 'module');
2084 if (requires && modules.hasOwnProperty(name)) {
2085 modules[name] = null;
2087 return ensure(modules, name, function() {
2089 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2090 "the module name or forgot to load it. If registering a module ensure that you " +
2091 "specify the dependencies as the second argument.", name);
2094 /** @type {!Array.<Array.<*>>} */
2095 var invokeQueue = [];
2097 /** @type {!Array.<Function>} */
2098 var configBlocks = [];
2100 /** @type {!Array.<Function>} */
2103 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2105 /** @type {angular.Module} */
2106 var moduleInstance = {
2108 _invokeQueue: invokeQueue,
2109 _configBlocks: configBlocks,
2110 _runBlocks: runBlocks,
2114 * @name angular.Module#requires
2118 * Holds the list of modules which the injector will load before the current module is
2125 * @name angular.Module#name
2129 * Name of the module.
2136 * @name angular.Module#provider
2138 * @param {string} name service name
2139 * @param {Function} providerType Construction function for creating new instance of the
2142 * See {@link auto.$provide#provider $provide.provider()}.
2144 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2148 * @name angular.Module#factory
2150 * @param {string} name service name
2151 * @param {Function} providerFunction Function for creating new instance of the service.
2153 * See {@link auto.$provide#factory $provide.factory()}.
2155 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2159 * @name angular.Module#service
2161 * @param {string} name service name
2162 * @param {Function} constructor A constructor function that will be instantiated.
2164 * See {@link auto.$provide#service $provide.service()}.
2166 service: invokeLaterAndSetModuleName('$provide', 'service'),
2170 * @name angular.Module#value
2172 * @param {string} name service name
2173 * @param {*} object Service instance object.
2175 * See {@link auto.$provide#value $provide.value()}.
2177 value: invokeLater('$provide', 'value'),
2181 * @name angular.Module#constant
2183 * @param {string} name constant name
2184 * @param {*} object Constant value.
2186 * Because the constants are fixed, they get applied before other provide methods.
2187 * See {@link auto.$provide#constant $provide.constant()}.
2189 constant: invokeLater('$provide', 'constant', 'unshift'),
2193 * @name angular.Module#decorator
2195 * @param {string} The name of the service to decorate.
2196 * @param {Function} This function will be invoked when the service needs to be
2197 * instantiated and should return the decorated service instance.
2199 * See {@link auto.$provide#decorator $provide.decorator()}.
2201 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2205 * @name angular.Module#animation
2207 * @param {string} name animation name
2208 * @param {Function} animationFactory Factory function for creating new instance of an
2212 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2215 * Defines an animation hook that can be later used with
2216 * {@link $animate $animate} service and directives that use this service.
2219 * module.animation('.animation-name', function($inject1, $inject2) {
2221 * eventName : function(element, done) {
2222 * //code to run the animation
2223 * //once complete, then run done()
2224 * return function cancellationFunction(element) {
2225 * //code to cancel the animation
2232 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2233 * {@link ngAnimate ngAnimate module} for more information.
2235 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2239 * @name angular.Module#filter
2241 * @param {string} name Filter name - this must be a valid angular expression identifier
2242 * @param {Function} filterFactory Factory function for creating new instance of filter.
2244 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2246 * <div class="alert alert-warning">
2247 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2248 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2249 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2250 * (`myapp_subsection_filterx`).
2253 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2257 * @name angular.Module#controller
2259 * @param {string|Object} name Controller name, or an object map of controllers where the
2260 * keys are the names and the values are the constructors.
2261 * @param {Function} constructor Controller constructor function.
2263 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2265 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2269 * @name angular.Module#directive
2271 * @param {string|Object} name Directive name, or an object map of directives where the
2272 * keys are the names and the values are the factories.
2273 * @param {Function} directiveFactory Factory function for creating new instance of
2276 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2278 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2282 * @name angular.Module#config
2284 * @param {Function} configFn Execute this function on module load. Useful for service
2287 * Use this method to register work which needs to be performed on module loading.
2288 * For more about how to configure services, see
2289 * {@link providers#provider-recipe Provider Recipe}.
2295 * @name angular.Module#run
2297 * @param {Function} initializationFn Execute this function after injector creation.
2298 * Useful for application initialization.
2300 * Use this method to register work which should be performed when the injector is done
2301 * loading all modules.
2303 run: function(block) {
2304 runBlocks.push(block);
2313 return moduleInstance;
2316 * @param {string} provider
2317 * @param {string} method
2318 * @param {String=} insertMethod
2319 * @returns {angular.Module}
2321 function invokeLater(provider, method, insertMethod, queue) {
2322 if (!queue) queue = invokeQueue;
2324 queue[insertMethod || 'push']([provider, method, arguments]);
2325 return moduleInstance;
2330 * @param {string} provider
2331 * @param {string} method
2332 * @returns {angular.Module}
2334 function invokeLaterAndSetModuleName(provider, method) {
2335 return function(recipeName, factoryFunction) {
2336 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2337 invokeQueue.push([provider, method, arguments]);
2338 return moduleInstance;
2347 /* global: toDebugString: true */
2349 function serializeObject(obj) {
2352 return JSON.stringify(obj, function(key, val) {
2353 val = toJsonReplacer(key, val);
2354 if (isObject(val)) {
2356 if (seen.indexOf(val) >= 0) return '...';
2364 function toDebugString(obj) {
2365 if (typeof obj === 'function') {
2366 return obj.toString().replace(/ \{[\s\S]*$/, '');
2367 } else if (isUndefined(obj)) {
2369 } else if (typeof obj !== 'string') {
2370 return serializeObject(obj);
2375 /* global angularModule: true,
2380 htmlAnchorDirective,
2389 ngBindHtmlDirective,
2390 ngBindTemplateDirective,
2392 ngClassEvenDirective,
2393 ngClassOddDirective,
2395 ngControllerDirective,
2400 ngIncludeFillContentDirective,
2402 ngNonBindableDirective,
2403 ngPluralizeDirective,
2408 ngSwitchWhenDirective,
2409 ngSwitchDefaultDirective,
2411 ngTranscludeDirective,
2424 ngModelOptionsDirective,
2425 ngAttributeAliasDirectives,
2428 $AnchorScrollProvider,
2430 $CoreAnimateCssProvider,
2431 $$CoreAnimateQueueProvider,
2432 $$CoreAnimateRunnerProvider,
2434 $CacheFactoryProvider,
2435 $ControllerProvider,
2437 $ExceptionHandlerProvider,
2439 $$ForceReflowProvider,
2440 $InterpolateProvider,
2444 $HttpParamSerializerProvider,
2445 $HttpParamSerializerJQLikeProvider,
2446 $HttpBackendProvider,
2447 $xhrFactoryProvider,
2454 $$SanitizeUriProvider,
2456 $SceDelegateProvider,
2458 $TemplateCacheProvider,
2459 $TemplateRequestProvider,
2460 $$TestabilityProvider,
2465 $$CookieReaderProvider
2471 * @name angular.version
2474 * An object that contains information about the current AngularJS version.
2476 * This object has the following properties:
2478 * - `full` – `{string}` – Full version string, such as "0.9.18".
2479 * - `major` – `{number}` – Major version number, such as "0".
2480 * - `minor` – `{number}` – Minor version number, such as "9".
2481 * - `dot` – `{number}` – Dot version number, such as "18".
2482 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2485 full: '1.4.8', // all of these placeholder strings will be replaced by grunt's
2486 major: 1, // package task
2489 codeName: 'ice-manipulation'
2493 function publishExternalAPI(angular) {
2495 'bootstrap': bootstrap,
2502 'injector': createInjector,
2506 'fromJson': fromJson,
2507 'identity': identity,
2508 'isUndefined': isUndefined,
2509 'isDefined': isDefined,
2510 'isString': isString,
2511 'isFunction': isFunction,
2512 'isObject': isObject,
2513 'isNumber': isNumber,
2514 'isElement': isElement,
2518 'lowercase': lowercase,
2519 'uppercase': uppercase,
2520 'callbacks': {counter: 0},
2521 'getTestability': getTestability,
2524 'reloadWithDebugInfo': reloadWithDebugInfo
2527 angularModule = setupModuleLoader(window);
2529 angularModule('ng', ['ngLocale'], ['$provide',
2530 function ngModule($provide) {
2531 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2533 $$sanitizeUri: $$SanitizeUriProvider
2535 $provide.provider('$compile', $CompileProvider).
2537 a: htmlAnchorDirective,
2538 input: inputDirective,
2539 textarea: inputDirective,
2540 form: formDirective,
2541 script: scriptDirective,
2542 select: selectDirective,
2543 style: styleDirective,
2544 option: optionDirective,
2545 ngBind: ngBindDirective,
2546 ngBindHtml: ngBindHtmlDirective,
2547 ngBindTemplate: ngBindTemplateDirective,
2548 ngClass: ngClassDirective,
2549 ngClassEven: ngClassEvenDirective,
2550 ngClassOdd: ngClassOddDirective,
2551 ngCloak: ngCloakDirective,
2552 ngController: ngControllerDirective,
2553 ngForm: ngFormDirective,
2554 ngHide: ngHideDirective,
2555 ngIf: ngIfDirective,
2556 ngInclude: ngIncludeDirective,
2557 ngInit: ngInitDirective,
2558 ngNonBindable: ngNonBindableDirective,
2559 ngPluralize: ngPluralizeDirective,
2560 ngRepeat: ngRepeatDirective,
2561 ngShow: ngShowDirective,
2562 ngStyle: ngStyleDirective,
2563 ngSwitch: ngSwitchDirective,
2564 ngSwitchWhen: ngSwitchWhenDirective,
2565 ngSwitchDefault: ngSwitchDefaultDirective,
2566 ngOptions: ngOptionsDirective,
2567 ngTransclude: ngTranscludeDirective,
2568 ngModel: ngModelDirective,
2569 ngList: ngListDirective,
2570 ngChange: ngChangeDirective,
2571 pattern: patternDirective,
2572 ngPattern: patternDirective,
2573 required: requiredDirective,
2574 ngRequired: requiredDirective,
2575 minlength: minlengthDirective,
2576 ngMinlength: minlengthDirective,
2577 maxlength: maxlengthDirective,
2578 ngMaxlength: maxlengthDirective,
2579 ngValue: ngValueDirective,
2580 ngModelOptions: ngModelOptionsDirective
2583 ngInclude: ngIncludeFillContentDirective
2585 directive(ngAttributeAliasDirectives).
2586 directive(ngEventDirectives);
2588 $anchorScroll: $AnchorScrollProvider,
2589 $animate: $AnimateProvider,
2590 $animateCss: $CoreAnimateCssProvider,
2591 $$animateQueue: $$CoreAnimateQueueProvider,
2592 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2593 $browser: $BrowserProvider,
2594 $cacheFactory: $CacheFactoryProvider,
2595 $controller: $ControllerProvider,
2596 $document: $DocumentProvider,
2597 $exceptionHandler: $ExceptionHandlerProvider,
2598 $filter: $FilterProvider,
2599 $$forceReflow: $$ForceReflowProvider,
2600 $interpolate: $InterpolateProvider,
2601 $interval: $IntervalProvider,
2602 $http: $HttpProvider,
2603 $httpParamSerializer: $HttpParamSerializerProvider,
2604 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2605 $httpBackend: $HttpBackendProvider,
2606 $xhrFactory: $xhrFactoryProvider,
2607 $location: $LocationProvider,
2609 $parse: $ParseProvider,
2610 $rootScope: $RootScopeProvider,
2614 $sceDelegate: $SceDelegateProvider,
2615 $sniffer: $SnifferProvider,
2616 $templateCache: $TemplateCacheProvider,
2617 $templateRequest: $TemplateRequestProvider,
2618 $$testability: $$TestabilityProvider,
2619 $timeout: $TimeoutProvider,
2620 $window: $WindowProvider,
2621 $$rAF: $$RAFProvider,
2622 $$jqLite: $$jqLiteProvider,
2623 $$HashMap: $$HashMapProvider,
2624 $$cookieReader: $$CookieReaderProvider
2630 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2631 * Any commits to this file should be reviewed with security in mind. *
2632 * Changes to this file can potentially create security vulnerabilities. *
2633 * An approval from 2 Core members with history of modifying *
2634 * this file is required. *
2636 * Does the change somehow allow for arbitrary javascript to be executed? *
2637 * Or allows for someone to change the prototype of built-in objects? *
2638 * Or gives undesired access to variables likes document or window? *
2639 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2641 /* global JQLitePrototype: true,
2642 addEventListenerFn: true,
2643 removeEventListenerFn: true,
2648 //////////////////////////////////
2650 //////////////////////////////////
2654 * @name angular.element
2659 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2661 * If jQuery is available, `angular.element` is an alias for the
2662 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2663 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2665 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2666 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2667 * commonly needed functionality with the goal of having a very small footprint.</div>
2669 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2671 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2672 * jqLite; they are never raw DOM references.</div>
2674 * ## Angular's jqLite
2675 * jqLite provides only the following jQuery methods:
2677 * - [`addClass()`](http://api.jquery.com/addClass/)
2678 * - [`after()`](http://api.jquery.com/after/)
2679 * - [`append()`](http://api.jquery.com/append/)
2680 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2681 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2682 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2683 * - [`clone()`](http://api.jquery.com/clone/)
2684 * - [`contents()`](http://api.jquery.com/contents/)
2685 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2686 * - [`data()`](http://api.jquery.com/data/)
2687 * - [`detach()`](http://api.jquery.com/detach/)
2688 * - [`empty()`](http://api.jquery.com/empty/)
2689 * - [`eq()`](http://api.jquery.com/eq/)
2690 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2691 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2692 * - [`html()`](http://api.jquery.com/html/)
2693 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2694 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2695 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2696 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2697 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2698 * - [`prepend()`](http://api.jquery.com/prepend/)
2699 * - [`prop()`](http://api.jquery.com/prop/)
2700 * - [`ready()`](http://api.jquery.com/ready/)
2701 * - [`remove()`](http://api.jquery.com/remove/)
2702 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2703 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2704 * - [`removeData()`](http://api.jquery.com/removeData/)
2705 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2706 * - [`text()`](http://api.jquery.com/text/)
2707 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2708 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2709 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2710 * - [`val()`](http://api.jquery.com/val/)
2711 * - [`wrap()`](http://api.jquery.com/wrap/)
2713 * ## jQuery/jqLite Extras
2714 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2717 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2718 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2719 * element before it is removed.
2722 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2723 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2724 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2726 * - `injector()` - retrieves the injector of the current element or its parent.
2727 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2728 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2730 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2731 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2732 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2733 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2734 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2735 * parent element is reached.
2737 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2738 * @returns {Object} jQuery object.
2741 JQLite.expando = 'ng339';
2743 var jqCache = JQLite.cache = {},
2745 addEventListenerFn = function(element, type, fn) {
2746 element.addEventListener(type, fn, false);
2748 removeEventListenerFn = function(element, type, fn) {
2749 element.removeEventListener(type, fn, false);
2753 * !!! This is an undocumented "private" function !!!
2755 JQLite._data = function(node) {
2756 //jQuery always returns an object on cache miss
2757 return this.cache[node[this.expando]] || {};
2760 function jqNextId() { return ++jqId; }
2763 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2764 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2765 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2766 var jqLiteMinErr = minErr('jqLite');
2769 * Converts snake_case to camelCase.
2770 * Also there is special case for Moz prefix starting with upper case letter.
2771 * @param name Name to normalize
2773 function camelCase(name) {
2775 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2776 return offset ? letter.toUpperCase() : letter;
2778 replace(MOZ_HACK_REGEXP, 'Moz$1');
2781 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2782 var HTML_REGEXP = /<|&#?\w+;/;
2783 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2784 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2787 'option': [1, '<select multiple="multiple">', '</select>'],
2789 'thead': [1, '<table>', '</table>'],
2790 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2791 'tr': [2, '<table><tbody>', '</tbody></table>'],
2792 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2793 '_default': [0, "", ""]
2796 wrapMap.optgroup = wrapMap.option;
2797 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2798 wrapMap.th = wrapMap.td;
2801 function jqLiteIsTextNode(html) {
2802 return !HTML_REGEXP.test(html);
2805 function jqLiteAcceptsData(node) {
2806 // The window object can accept data but has no nodeType
2807 // Otherwise we are only interested in elements (1) and documents (9)
2808 var nodeType = node.nodeType;
2809 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2812 function jqLiteHasData(node) {
2813 for (var key in jqCache[node.ng339]) {
2819 function jqLiteBuildFragment(html, context) {
2821 fragment = context.createDocumentFragment(),
2824 if (jqLiteIsTextNode(html)) {
2825 // Convert non-html into a text node
2826 nodes.push(context.createTextNode(html));
2828 // Convert html into DOM nodes
2829 tmp = tmp || fragment.appendChild(context.createElement("div"));
2830 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2831 wrap = wrapMap[tag] || wrapMap._default;
2832 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2834 // Descend through wrappers to the right content
2837 tmp = tmp.lastChild;
2840 nodes = concat(nodes, tmp.childNodes);
2842 tmp = fragment.firstChild;
2843 tmp.textContent = "";
2846 // Remove wrapper from fragment
2847 fragment.textContent = "";
2848 fragment.innerHTML = ""; // Clear inner HTML
2849 forEach(nodes, function(node) {
2850 fragment.appendChild(node);
2856 function jqLiteParseHTML(html, context) {
2857 context = context || document;
2860 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2861 return [context.createElement(parsed[1])];
2864 if ((parsed = jqLiteBuildFragment(html, context))) {
2865 return parsed.childNodes;
2872 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2873 var jqLiteContains = Node.prototype.contains || function(arg) {
2874 // jshint bitwise: false
2875 return !!(this.compareDocumentPosition(arg) & 16);
2876 // jshint bitwise: true
2879 /////////////////////////////////////////////
2880 function JQLite(element) {
2881 if (element instanceof JQLite) {
2887 if (isString(element)) {
2888 element = trim(element);
2891 if (!(this instanceof JQLite)) {
2892 if (argIsString && element.charAt(0) != '<') {
2893 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2895 return new JQLite(element);
2899 jqLiteAddNodes(this, jqLiteParseHTML(element));
2901 jqLiteAddNodes(this, element);
2905 function jqLiteClone(element) {
2906 return element.cloneNode(true);
2909 function jqLiteDealoc(element, onlyDescendants) {
2910 if (!onlyDescendants) jqLiteRemoveData(element);
2912 if (element.querySelectorAll) {
2913 var descendants = element.querySelectorAll('*');
2914 for (var i = 0, l = descendants.length; i < l; i++) {
2915 jqLiteRemoveData(descendants[i]);
2920 function jqLiteOff(element, type, fn, unsupported) {
2921 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2923 var expandoStore = jqLiteExpandoStore(element);
2924 var events = expandoStore && expandoStore.events;
2925 var handle = expandoStore && expandoStore.handle;
2927 if (!handle) return; //no listeners registered
2930 for (type in events) {
2931 if (type !== '$destroy') {
2932 removeEventListenerFn(element, type, handle);
2934 delete events[type];
2938 var removeHandler = function(type) {
2939 var listenerFns = events[type];
2940 if (isDefined(fn)) {
2941 arrayRemove(listenerFns || [], fn);
2943 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2944 removeEventListenerFn(element, type, handle);
2945 delete events[type];
2949 forEach(type.split(' '), function(type) {
2950 removeHandler(type);
2951 if (MOUSE_EVENT_MAP[type]) {
2952 removeHandler(MOUSE_EVENT_MAP[type]);
2958 function jqLiteRemoveData(element, name) {
2959 var expandoId = element.ng339;
2960 var expandoStore = expandoId && jqCache[expandoId];
2964 delete expandoStore.data[name];
2968 if (expandoStore.handle) {
2969 if (expandoStore.events.$destroy) {
2970 expandoStore.handle({}, '$destroy');
2974 delete jqCache[expandoId];
2975 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2980 function jqLiteExpandoStore(element, createIfNecessary) {
2981 var expandoId = element.ng339,
2982 expandoStore = expandoId && jqCache[expandoId];
2984 if (createIfNecessary && !expandoStore) {
2985 element.ng339 = expandoId = jqNextId();
2986 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2989 return expandoStore;
2993 function jqLiteData(element, key, value) {
2994 if (jqLiteAcceptsData(element)) {
2996 var isSimpleSetter = isDefined(value);
2997 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2998 var massGetter = !key;
2999 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3000 var data = expandoStore && expandoStore.data;
3002 if (isSimpleSetter) { // data('key', value)
3005 if (massGetter) { // data()
3008 if (isSimpleGetter) { // data('key')
3009 // don't force creation of expandoStore if it doesn't exist yet
3010 return data && data[key];
3011 } else { // mass-setter: data({key1: val1, key2: val2})
3019 function jqLiteHasClass(element, selector) {
3020 if (!element.getAttribute) return false;
3021 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3022 indexOf(" " + selector + " ") > -1);
3025 function jqLiteRemoveClass(element, cssClasses) {
3026 if (cssClasses && element.setAttribute) {
3027 forEach(cssClasses.split(' '), function(cssClass) {
3028 element.setAttribute('class', trim(
3029 (" " + (element.getAttribute('class') || '') + " ")
3030 .replace(/[\n\t]/g, " ")
3031 .replace(" " + trim(cssClass) + " ", " "))
3037 function jqLiteAddClass(element, cssClasses) {
3038 if (cssClasses && element.setAttribute) {
3039 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3040 .replace(/[\n\t]/g, " ");
3042 forEach(cssClasses.split(' '), function(cssClass) {
3043 cssClass = trim(cssClass);
3044 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3045 existingClasses += cssClass + ' ';
3049 element.setAttribute('class', trim(existingClasses));
3054 function jqLiteAddNodes(root, elements) {
3055 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3059 // if a Node (the most common case)
3060 if (elements.nodeType) {
3061 root[root.length++] = elements;
3063 var length = elements.length;
3065 // if an Array or NodeList and not a Window
3066 if (typeof length === 'number' && elements.window !== elements) {
3068 for (var i = 0; i < length; i++) {
3069 root[root.length++] = elements[i];
3073 root[root.length++] = elements;
3080 function jqLiteController(element, name) {
3081 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3084 function jqLiteInheritedData(element, name, value) {
3085 // if element is the document object work with the html element instead
3086 // this makes $(document).scope() possible
3087 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3088 element = element.documentElement;
3090 var names = isArray(name) ? name : [name];
3093 for (var i = 0, ii = names.length; i < ii; i++) {
3094 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3097 // If dealing with a document fragment node with a host element, and no parent, use the host
3098 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3099 // to lookup parent controllers.
3100 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3104 function jqLiteEmpty(element) {
3105 jqLiteDealoc(element, true);
3106 while (element.firstChild) {
3107 element.removeChild(element.firstChild);
3111 function jqLiteRemove(element, keepData) {
3112 if (!keepData) jqLiteDealoc(element);
3113 var parent = element.parentNode;
3114 if (parent) parent.removeChild(element);
3118 function jqLiteDocumentLoaded(action, win) {
3119 win = win || window;
3120 if (win.document.readyState === 'complete') {
3121 // Force the action to be run async for consistent behaviour
3122 // from the action's point of view
3123 // i.e. it will definitely not be in a $apply
3124 win.setTimeout(action);
3126 // No need to unbind this handler as load is only ever called once
3127 jqLite(win).on('load', action);
3131 //////////////////////////////////////////
3132 // Functions which are declared directly.
3133 //////////////////////////////////////////
3134 var JQLitePrototype = JQLite.prototype = {
3135 ready: function(fn) {
3138 function trigger() {
3144 // check if document is already loaded
3145 if (document.readyState === 'complete') {
3146 setTimeout(trigger);
3148 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3149 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3151 JQLite(window).on('load', trigger); // fallback to window.onload for others
3155 toString: function() {
3157 forEach(this, function(e) { value.push('' + e);});
3158 return '[' + value.join(', ') + ']';
3161 eq: function(index) {
3162 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3171 //////////////////////////////////////////
3172 // Functions iterating getter/setters.
3173 // these functions return self on setter and
3175 //////////////////////////////////////////
3176 var BOOLEAN_ATTR = {};
3177 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3178 BOOLEAN_ATTR[lowercase(value)] = value;
3180 var BOOLEAN_ELEMENTS = {};
3181 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3182 BOOLEAN_ELEMENTS[value] = true;
3184 var ALIASED_ATTR = {
3185 'ngMinlength': 'minlength',
3186 'ngMaxlength': 'maxlength',
3189 'ngPattern': 'pattern'
3192 function getBooleanAttrName(element, name) {
3193 // check dom last since we will most likely fail on name
3194 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3196 // booleanAttr is here twice to minimize DOM access
3197 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3200 function getAliasedAttrName(name) {
3201 return ALIASED_ATTR[name];
3206 removeData: jqLiteRemoveData,
3207 hasData: jqLiteHasData
3208 }, function(fn, name) {
3214 inheritedData: jqLiteInheritedData,
3216 scope: function(element) {
3217 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3218 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3221 isolateScope: function(element) {
3222 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3223 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3226 controller: jqLiteController,
3228 injector: function(element) {
3229 return jqLiteInheritedData(element, '$injector');
3232 removeAttr: function(element, name) {
3233 element.removeAttribute(name);
3236 hasClass: jqLiteHasClass,
3238 css: function(element, name, value) {
3239 name = camelCase(name);
3241 if (isDefined(value)) {
3242 element.style[name] = value;
3244 return element.style[name];
3248 attr: function(element, name, value) {
3249 var nodeType = element.nodeType;
3250 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3253 var lowercasedName = lowercase(name);
3254 if (BOOLEAN_ATTR[lowercasedName]) {
3255 if (isDefined(value)) {
3257 element[name] = true;
3258 element.setAttribute(name, lowercasedName);
3260 element[name] = false;
3261 element.removeAttribute(lowercasedName);
3264 return (element[name] ||
3265 (element.attributes.getNamedItem(name) || noop).specified)
3269 } else if (isDefined(value)) {
3270 element.setAttribute(name, value);
3271 } else if (element.getAttribute) {
3272 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3273 // some elements (e.g. Document) don't have get attribute, so return undefined
3274 var ret = element.getAttribute(name, 2);
3275 // normalize non-existing attributes to undefined (as jQuery)
3276 return ret === null ? undefined : ret;
3280 prop: function(element, name, value) {
3281 if (isDefined(value)) {
3282 element[name] = value;
3284 return element[name];
3292 function getText(element, value) {
3293 if (isUndefined(value)) {
3294 var nodeType = element.nodeType;
3295 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3297 element.textContent = value;
3301 val: function(element, value) {
3302 if (isUndefined(value)) {
3303 if (element.multiple && nodeName_(element) === 'select') {
3305 forEach(element.options, function(option) {
3306 if (option.selected) {
3307 result.push(option.value || option.text);
3310 return result.length === 0 ? null : result;
3312 return element.value;
3314 element.value = value;
3317 html: function(element, value) {
3318 if (isUndefined(value)) {
3319 return element.innerHTML;
3321 jqLiteDealoc(element, true);
3322 element.innerHTML = value;
3326 }, function(fn, name) {
3328 * Properties: writes return selection, reads return first value
3330 JQLite.prototype[name] = function(arg1, arg2) {
3332 var nodeCount = this.length;
3334 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3335 // in a way that survives minification.
3336 // jqLiteEmpty takes no arguments but is a setter.
3337 if (fn !== jqLiteEmpty &&
3338 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3339 if (isObject(arg1)) {
3341 // we are a write, but the object properties are the key/values
3342 for (i = 0; i < nodeCount; i++) {
3343 if (fn === jqLiteData) {
3344 // data() takes the whole object in jQuery
3348 fn(this[i], key, arg1[key]);
3352 // return self for chaining
3355 // we are a read, so read the first child.
3356 // TODO: do we still need this?
3358 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3359 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3360 for (var j = 0; j < jj; j++) {
3361 var nodeValue = fn(this[j], arg1, arg2);
3362 value = value ? value + nodeValue : nodeValue;
3367 // we are a write, so apply to all children
3368 for (i = 0; i < nodeCount; i++) {
3369 fn(this[i], arg1, arg2);
3371 // return self for chaining
3377 function createEventHandler(element, events) {
3378 var eventHandler = function(event, type) {
3379 // jQuery specific api
3380 event.isDefaultPrevented = function() {
3381 return event.defaultPrevented;
3384 var eventFns = events[type || event.type];
3385 var eventFnsLength = eventFns ? eventFns.length : 0;
3387 if (!eventFnsLength) return;
3389 if (isUndefined(event.immediatePropagationStopped)) {
3390 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3391 event.stopImmediatePropagation = function() {
3392 event.immediatePropagationStopped = true;
3394 if (event.stopPropagation) {
3395 event.stopPropagation();
3398 if (originalStopImmediatePropagation) {
3399 originalStopImmediatePropagation.call(event);
3404 event.isImmediatePropagationStopped = function() {
3405 return event.immediatePropagationStopped === true;
3408 // Some events have special handlers that wrap the real handler
3409 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3411 // Copy event handlers in case event handlers array is modified during execution.
3412 if ((eventFnsLength > 1)) {
3413 eventFns = shallowCopy(eventFns);
3416 for (var i = 0; i < eventFnsLength; i++) {
3417 if (!event.isImmediatePropagationStopped()) {
3418 handlerWrapper(element, event, eventFns[i]);
3423 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3424 // events on `element`
3425 eventHandler.elem = element;
3426 return eventHandler;
3429 function defaultHandlerWrapper(element, event, handler) {
3430 handler.call(element, event);
3433 function specialMouseHandlerWrapper(target, event, handler) {
3434 // Refer to jQuery's implementation of mouseenter & mouseleave
3435 // Read about mouseenter and mouseleave:
3436 // http://www.quirksmode.org/js/events_mouse.html#link8
3437 var related = event.relatedTarget;
3438 // For mousenter/leave call the handler if related is outside the target.
3439 // NB: No relatedTarget if the mouse left/entered the browser window
3440 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3441 handler.call(target, event);
3445 //////////////////////////////////////////
3446 // Functions iterating traversal.
3447 // These functions chain results into a single
3449 //////////////////////////////////////////
3451 removeData: jqLiteRemoveData,
3453 on: function jqLiteOn(element, type, fn, unsupported) {
3454 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3456 // Do not add event handlers to non-elements because they will not be cleaned up.
3457 if (!jqLiteAcceptsData(element)) {
3461 var expandoStore = jqLiteExpandoStore(element, true);
3462 var events = expandoStore.events;
3463 var handle = expandoStore.handle;
3466 handle = expandoStore.handle = createEventHandler(element, events);
3469 // http://jsperf.com/string-indexof-vs-split
3470 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3471 var i = types.length;
3473 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3474 var eventFns = events[type];
3477 eventFns = events[type] = [];
3478 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3479 if (type !== '$destroy' && !noEventListener) {
3480 addEventListenerFn(element, type, handle);
3489 if (MOUSE_EVENT_MAP[type]) {
3490 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3491 addHandler(type, undefined, true);
3500 one: function(element, type, fn) {
3501 element = jqLite(element);
3503 //add the listener twice so that when it is called
3504 //you can remove the original function and still be
3505 //able to call element.off(ev, fn) normally
3506 element.on(type, function onFn() {
3507 element.off(type, fn);
3508 element.off(type, onFn);
3510 element.on(type, fn);
3513 replaceWith: function(element, replaceNode) {
3514 var index, parent = element.parentNode;
3515 jqLiteDealoc(element);
3516 forEach(new JQLite(replaceNode), function(node) {
3518 parent.insertBefore(node, index.nextSibling);
3520 parent.replaceChild(node, element);
3526 children: function(element) {
3528 forEach(element.childNodes, function(element) {
3529 if (element.nodeType === NODE_TYPE_ELEMENT) {
3530 children.push(element);
3536 contents: function(element) {
3537 return element.contentDocument || element.childNodes || [];
3540 append: function(element, node) {
3541 var nodeType = element.nodeType;
3542 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3544 node = new JQLite(node);
3546 for (var i = 0, ii = node.length; i < ii; i++) {
3547 var child = node[i];
3548 element.appendChild(child);
3552 prepend: function(element, node) {
3553 if (element.nodeType === NODE_TYPE_ELEMENT) {
3554 var index = element.firstChild;
3555 forEach(new JQLite(node), function(child) {
3556 element.insertBefore(child, index);
3561 wrap: function(element, wrapNode) {
3562 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3563 var parent = element.parentNode;
3565 parent.replaceChild(wrapNode, element);
3567 wrapNode.appendChild(element);
3570 remove: jqLiteRemove,
3572 detach: function(element) {
3573 jqLiteRemove(element, true);
3576 after: function(element, newElement) {
3577 var index = element, parent = element.parentNode;
3578 newElement = new JQLite(newElement);
3580 for (var i = 0, ii = newElement.length; i < ii; i++) {
3581 var node = newElement[i];
3582 parent.insertBefore(node, index.nextSibling);
3587 addClass: jqLiteAddClass,
3588 removeClass: jqLiteRemoveClass,
3590 toggleClass: function(element, selector, condition) {
3592 forEach(selector.split(' '), function(className) {
3593 var classCondition = condition;
3594 if (isUndefined(classCondition)) {
3595 classCondition = !jqLiteHasClass(element, className);
3597 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3602 parent: function(element) {
3603 var parent = element.parentNode;
3604 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3607 next: function(element) {
3608 return element.nextElementSibling;
3611 find: function(element, selector) {
3612 if (element.getElementsByTagName) {
3613 return element.getElementsByTagName(selector);
3621 triggerHandler: function(element, event, extraParameters) {
3623 var dummyEvent, eventFnsCopy, handlerArgs;
3624 var eventName = event.type || event;
3625 var expandoStore = jqLiteExpandoStore(element);
3626 var events = expandoStore && expandoStore.events;
3627 var eventFns = events && events[eventName];
3630 // Create a dummy event to pass to the handlers
3632 preventDefault: function() { this.defaultPrevented = true; },
3633 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3634 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3635 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3636 stopPropagation: noop,
3641 // If a custom event was provided then extend our dummy event with it
3643 dummyEvent = extend(dummyEvent, event);
3646 // Copy event handlers in case event handlers array is modified during execution.
3647 eventFnsCopy = shallowCopy(eventFns);
3648 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3650 forEach(eventFnsCopy, function(fn) {
3651 if (!dummyEvent.isImmediatePropagationStopped()) {
3652 fn.apply(element, handlerArgs);
3657 }, function(fn, name) {
3659 * chaining functions
3661 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3664 for (var i = 0, ii = this.length; i < ii; i++) {
3665 if (isUndefined(value)) {
3666 value = fn(this[i], arg1, arg2, arg3);
3667 if (isDefined(value)) {
3668 // any function which returns a value needs to be wrapped
3669 value = jqLite(value);
3672 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3675 return isDefined(value) ? value : this;
3678 // bind legacy bind/unbind to on/off
3679 JQLite.prototype.bind = JQLite.prototype.on;
3680 JQLite.prototype.unbind = JQLite.prototype.off;
3684 // Provider for private $$jqLite service
3685 function $$jqLiteProvider() {
3686 this.$get = function $$jqLite() {
3687 return extend(JQLite, {
3688 hasClass: function(node, classes) {
3689 if (node.attr) node = node[0];
3690 return jqLiteHasClass(node, classes);
3692 addClass: function(node, classes) {
3693 if (node.attr) node = node[0];
3694 return jqLiteAddClass(node, classes);
3696 removeClass: function(node, classes) {
3697 if (node.attr) node = node[0];
3698 return jqLiteRemoveClass(node, classes);
3705 * Computes a hash of an 'obj'.
3708 * number is number as string
3709 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3710 * that is also assigned to the $$hashKey property of the object.
3713 * @returns {string} hash string such that the same input will have the same hash string.
3714 * The resulting string key is in 'type:hashKey' format.
3716 function hashKey(obj, nextUidFn) {
3717 var key = obj && obj.$$hashKey;
3720 if (typeof key === 'function') {
3721 key = obj.$$hashKey();
3726 var objType = typeof obj;
3727 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3728 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3730 key = objType + ':' + obj;
3737 * HashMap which can use objects as keys
3739 function HashMap(array, isolatedUid) {
3742 this.nextUid = function() {
3746 forEach(array, this.put, this);
3748 HashMap.prototype = {
3750 * Store key value pair
3751 * @param key key to store can be any type
3752 * @param value value to store can be any type
3754 put: function(key, value) {
3755 this[hashKey(key, this.nextUid)] = value;
3760 * @returns {Object} the value for the key
3762 get: function(key) {
3763 return this[hashKey(key, this.nextUid)];
3767 * Remove the key/value pair
3770 remove: function(key) {
3771 var value = this[key = hashKey(key, this.nextUid)];
3777 var $$HashMapProvider = [function() {
3778 this.$get = [function() {
3786 * @name angular.injector
3790 * Creates an injector object that can be used for retrieving services as well as for
3791 * dependency injection (see {@link guide/di dependency injection}).
3793 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3794 * {@link angular.module}. The `ng` module must be explicitly added.
3795 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3796 * disallows argument name annotation inference.
3797 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3802 * // create an injector
3803 * var $injector = angular.injector(['ng']);
3805 * // use the injector to kick off your application
3806 * // use the type inference to auto inject arguments, or use implicit injection
3807 * $injector.invoke(function($rootScope, $compile, $document) {
3808 * $compile($document)($rootScope);
3809 * $rootScope.$digest();
3813 * Sometimes you want to get access to the injector of a currently running Angular app
3814 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3815 * application has been bootstrapped. You can do this using the extra `injector()` added
3816 * to JQuery/jqLite elements. See {@link angular.element}.
3818 * *This is fairly rare but could be the case if a third party library is injecting the
3821 * In the following example a new block of HTML containing a `ng-controller`
3822 * directive is added to the end of the document body by JQuery. We then compile and link
3823 * it into the current AngularJS scope.
3826 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3827 * $(document.body).append($div);
3829 * angular.element(document).injector().invoke(function($compile) {
3830 * var scope = angular.element($div).scope();
3831 * $compile($div)(scope);
3842 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3845 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3846 var FN_ARG_SPLIT = /,/;
3847 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3848 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3849 var $injectorMinErr = minErr('$injector');
3851 function anonFn(fn) {
3852 // For anonymous functions, showing at the very least the function signature can help in
3854 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3855 args = fnText.match(FN_ARGS);
3857 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3862 function annotate(fn, strictDi, name) {
3868 if (typeof fn === 'function') {
3869 if (!($inject = fn.$inject)) {
3873 if (!isString(name) || !name) {
3874 name = fn.name || anonFn(fn);
3876 throw $injectorMinErr('strictdi',
3877 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3879 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3880 argDecl = fnText.match(FN_ARGS);
3881 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3882 arg.replace(FN_ARG, function(all, underscore, name) {
3887 fn.$inject = $inject;
3889 } else if (isArray(fn)) {
3890 last = fn.length - 1;
3891 assertArgFn(fn[last], 'fn');
3892 $inject = fn.slice(0, last);
3894 assertArgFn(fn, 'fn', true);
3899 ///////////////////////////////////////
3907 * `$injector` is used to retrieve object instances as defined by
3908 * {@link auto.$provide provider}, instantiate types, invoke methods,
3911 * The following always holds true:
3914 * var $injector = angular.injector();
3915 * expect($injector.get('$injector')).toBe($injector);
3916 * expect($injector.invoke(function($injector) {
3918 * })).toBe($injector);
3921 * # Injection Function Annotation
3923 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3924 * following are all valid ways of annotating function with injection arguments and are equivalent.
3927 * // inferred (only works if code not minified/obfuscated)
3928 * $injector.invoke(function(serviceA){});
3931 * function explicit(serviceA) {};
3932 * explicit.$inject = ['serviceA'];
3933 * $injector.invoke(explicit);
3936 * $injector.invoke(['serviceA', function(serviceA){}]);
3941 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3942 * can then be parsed and the function arguments can be extracted. This method of discovering
3943 * annotations is disallowed when the injector is in strict mode.
3944 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3947 * ## `$inject` Annotation
3948 * By adding an `$inject` property onto a function the injection parameters can be specified.
3951 * As an array of injection names, where the last item in the array is the function to call.
3956 * @name $injector#get
3959 * Return an instance of the service.
3961 * @param {string} name The name of the instance to retrieve.
3962 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3963 * @return {*} The instance.
3968 * @name $injector#invoke
3971 * Invoke the method and supply the method arguments from the `$injector`.
3973 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3974 * injected according to the {@link guide/di $inject Annotation} rules.
3975 * @param {Object=} self The `this` for the invoked method.
3976 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3977 * object first, before the `$injector` is consulted.
3978 * @returns {*} the value returned by the invoked `fn` function.
3983 * @name $injector#has
3986 * Allows the user to query if the particular service exists.
3988 * @param {string} name Name of the service to query.
3989 * @returns {boolean} `true` if injector has given service.
3994 * @name $injector#instantiate
3996 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3997 * operator, and supplies all of the arguments to the constructor function as specified by the
3998 * constructor annotation.
4000 * @param {Function} Type Annotated constructor function.
4001 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4002 * object first, before the `$injector` is consulted.
4003 * @returns {Object} new instance of `Type`.
4008 * @name $injector#annotate
4011 * Returns an array of service names which the function is requesting for injection. This API is
4012 * used by the injector to determine which services need to be injected into the function when the
4013 * function is invoked. There are three ways in which the function can be annotated with the needed
4018 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4019 * by converting the function into a string using `toString()` method and extracting the argument
4023 * function MyController($scope, $route) {
4028 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4031 * You can disallow this method by using strict injection mode.
4033 * This method does not work with code minification / obfuscation. For this reason the following
4034 * annotation strategies are supported.
4036 * # The `$inject` property
4038 * If a function has an `$inject` property and its value is an array of strings, then the strings
4039 * represent names of services to be injected into the function.
4042 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
4045 * // Define function dependencies
4046 * MyController['$inject'] = ['$scope', '$route'];
4049 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4052 * # The array notation
4054 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4055 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4056 * a way that survives minification is a better choice:
4059 * // We wish to write this (not minification / obfuscation safe)
4060 * injector.invoke(function($compile, $rootScope) {
4064 * // We are forced to write break inlining
4065 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4068 * tmpFn.$inject = ['$compile', '$rootScope'];
4069 * injector.invoke(tmpFn);
4071 * // To better support inline function the inline annotation is supported
4072 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4077 * expect(injector.annotate(
4078 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4079 * ).toEqual(['$compile', '$rootScope']);
4082 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4083 * be retrieved as described above.
4085 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4087 * @returns {Array.<string>} The names of the services which the function requires.
4099 * The {@link auto.$provide $provide} service has a number of methods for registering components
4100 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4101 * {@link angular.Module}.
4103 * An Angular **service** is a singleton object created by a **service factory**. These **service
4104 * factories** are functions which, in turn, are created by a **service provider**.
4105 * The **service providers** are constructor functions. When instantiated they must contain a
4106 * property called `$get`, which holds the **service factory** function.
4108 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4109 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4110 * function to get the instance of the **service**.
4112 * Often services have no configuration options and there is no need to add methods to the service
4113 * provider. The provider will be no more than a constructor function with a `$get` property. For
4114 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4115 * services without specifying a provider.
4117 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4118 * {@link auto.$injector $injector}
4119 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4120 * providers and services.
4121 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4122 * services, not providers.
4123 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4124 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4125 * given factory function.
4126 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4127 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4128 * a new object using the given constructor function.
4130 * See the individual methods for more information and examples.
4135 * @name $provide#provider
4138 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4139 * are constructor functions, whose instances are responsible for "providing" a factory for a
4142 * Service provider names start with the name of the service they provide followed by `Provider`.
4143 * For example, the {@link ng.$log $log} service has a provider called
4144 * {@link ng.$logProvider $logProvider}.
4146 * Service provider objects can have additional methods which allow configuration of the provider
4147 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4148 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4149 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4150 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4153 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4155 * @param {(Object|function())} provider If the provider is:
4157 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4158 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4159 * - `Constructor`: a new instance of the provider will be created using
4160 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4162 * @returns {Object} registered provider instance
4166 * The following example shows how to create a simple event tracking service and register it using
4167 * {@link auto.$provide#provider $provide.provider()}.
4170 * // Define the eventTracker provider
4171 * function EventTrackerProvider() {
4172 * var trackingUrl = '/track';
4174 * // A provider method for configuring where the tracked events should been saved
4175 * this.setTrackingUrl = function(url) {
4176 * trackingUrl = url;
4179 * // The service factory function
4180 * this.$get = ['$http', function($http) {
4181 * var trackedEvents = {};
4183 * // Call this to track an event
4184 * event: function(event) {
4185 * var count = trackedEvents[event] || 0;
4187 * trackedEvents[event] = count;
4190 * // Call this to save the tracked events to the trackingUrl
4191 * save: function() {
4192 * $http.post(trackingUrl, trackedEvents);
4198 * describe('eventTracker', function() {
4201 * beforeEach(module(function($provide) {
4202 * // Register the eventTracker provider
4203 * $provide.provider('eventTracker', EventTrackerProvider);
4206 * beforeEach(module(function(eventTrackerProvider) {
4207 * // Configure eventTracker provider
4208 * eventTrackerProvider.setTrackingUrl('/custom-track');
4211 * it('tracks events', inject(function(eventTracker) {
4212 * expect(eventTracker.event('login')).toEqual(1);
4213 * expect(eventTracker.event('login')).toEqual(2);
4216 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4217 * postSpy = spyOn($http, 'post');
4218 * eventTracker.event('login');
4219 * eventTracker.save();
4220 * expect(postSpy).toHaveBeenCalled();
4221 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4222 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4223 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4231 * @name $provide#factory
4234 * Register a **service factory**, which will be called to return the service instance.
4235 * This is short for registering a service where its provider consists of only a `$get` property,
4236 * which is the given service factory function.
4237 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4238 * configure your service in a provider.
4240 * @param {string} name The name of the instance.
4241 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4242 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4243 * @returns {Object} registered provider instance
4246 * Here is an example of registering a service
4248 * $provide.factory('ping', ['$http', function($http) {
4249 * return function ping() {
4250 * return $http.send('/ping');
4254 * You would then inject and use this service like this:
4256 * someModule.controller('Ctrl', ['ping', function(ping) {
4265 * @name $provide#service
4268 * Register a **service constructor**, which will be invoked with `new` to create the service
4270 * This is short for registering a service where its provider's `$get` property is the service
4271 * constructor function that will be used to instantiate the service instance.
4273 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4276 * @param {string} name The name of the instance.
4277 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4278 * that will be instantiated.
4279 * @returns {Object} registered provider instance
4282 * Here is an example of registering a service using
4283 * {@link auto.$provide#service $provide.service(class)}.
4285 * var Ping = function($http) {
4286 * this.$http = $http;
4289 * Ping.$inject = ['$http'];
4291 * Ping.prototype.send = function() {
4292 * return this.$http.get('/ping');
4294 * $provide.service('ping', Ping);
4296 * You would then inject and use this service like this:
4298 * someModule.controller('Ctrl', ['ping', function(ping) {
4307 * @name $provide#value
4310 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4311 * number, an array, an object or a function. This is short for registering a service where its
4312 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4315 * Value services are similar to constant services, except that they cannot be injected into a
4316 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4318 * {@link auto.$provide#decorator decorator}.
4320 * @param {string} name The name of the instance.
4321 * @param {*} value The value.
4322 * @returns {Object} registered provider instance
4325 * Here are some examples of creating value services.
4327 * $provide.value('ADMIN_USER', 'admin');
4329 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4331 * $provide.value('halfOf', function(value) {
4340 * @name $provide#constant
4343 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4344 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4345 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4346 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4348 * @param {string} name The name of the constant.
4349 * @param {*} value The constant value.
4350 * @returns {Object} registered instance
4353 * Here a some examples of creating constants:
4355 * $provide.constant('SHARD_HEIGHT', 306);
4357 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4359 * $provide.constant('double', function(value) {
4368 * @name $provide#decorator
4371 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4372 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4373 * service. The object returned by the decorator may be the original service, or a new service
4374 * object which replaces or wraps and delegates to the original service.
4376 * @param {string} name The name of the service to decorate.
4377 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4378 * instantiated and should return the decorated service instance. The function is called using
4379 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4380 * Local injection arguments:
4382 * * `$delegate` - The original service instance, which can be monkey patched, configured,
4383 * decorated or delegated to.
4386 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4387 * calls to {@link ng.$log#error $log.warn()}.
4389 * $provide.decorator('$log', ['$delegate', function($delegate) {
4390 * $delegate.warn = $delegate.error;
4397 function createInjector(modulesToLoad, strictDi) {
4398 strictDi = (strictDi === true);
4399 var INSTANTIATING = {},
4400 providerSuffix = 'Provider',
4402 loadedModules = new HashMap([], true),
4405 provider: supportObject(provider),
4406 factory: supportObject(factory),
4407 service: supportObject(service),
4408 value: supportObject(value),
4409 constant: supportObject(constant),
4410 decorator: decorator
4413 providerInjector = (providerCache.$injector =
4414 createInternalInjector(providerCache, function(serviceName, caller) {
4415 if (angular.isString(caller)) {
4418 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4421 instanceInjector = (instanceCache.$injector =
4422 createInternalInjector(instanceCache, function(serviceName, caller) {
4423 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4424 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4428 forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4430 return instanceInjector;
4432 ////////////////////////////////////
4434 ////////////////////////////////////
4436 function supportObject(delegate) {
4437 return function(key, value) {
4438 if (isObject(key)) {
4439 forEach(key, reverseParams(delegate));
4441 return delegate(key, value);
4446 function provider(name, provider_) {
4447 assertNotHasOwnProperty(name, 'service');
4448 if (isFunction(provider_) || isArray(provider_)) {
4449 provider_ = providerInjector.instantiate(provider_);
4451 if (!provider_.$get) {
4452 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4454 return providerCache[name + providerSuffix] = provider_;
4457 function enforceReturnValue(name, factory) {
4458 return function enforcedReturnValue() {
4459 var result = instanceInjector.invoke(factory, this);
4460 if (isUndefined(result)) {
4461 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4467 function factory(name, factoryFn, enforce) {
4468 return provider(name, {
4469 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4473 function service(name, constructor) {
4474 return factory(name, ['$injector', function($injector) {
4475 return $injector.instantiate(constructor);
4479 function value(name, val) { return factory(name, valueFn(val), false); }
4481 function constant(name, value) {
4482 assertNotHasOwnProperty(name, 'constant');
4483 providerCache[name] = value;
4484 instanceCache[name] = value;
4487 function decorator(serviceName, decorFn) {
4488 var origProvider = providerInjector.get(serviceName + providerSuffix),
4489 orig$get = origProvider.$get;
4491 origProvider.$get = function() {
4492 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4493 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4497 ////////////////////////////////////
4499 ////////////////////////////////////
4500 function loadModules(modulesToLoad) {
4501 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4502 var runBlocks = [], moduleFn;
4503 forEach(modulesToLoad, function(module) {
4504 if (loadedModules.get(module)) return;
4505 loadedModules.put(module, true);
4507 function runInvokeQueue(queue) {
4509 for (i = 0, ii = queue.length; i < ii; i++) {
4510 var invokeArgs = queue[i],
4511 provider = providerInjector.get(invokeArgs[0]);
4513 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4518 if (isString(module)) {
4519 moduleFn = angularModule(module);
4520 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4521 runInvokeQueue(moduleFn._invokeQueue);
4522 runInvokeQueue(moduleFn._configBlocks);
4523 } else if (isFunction(module)) {
4524 runBlocks.push(providerInjector.invoke(module));
4525 } else if (isArray(module)) {
4526 runBlocks.push(providerInjector.invoke(module));
4528 assertArgFn(module, 'module');
4531 if (isArray(module)) {
4532 module = module[module.length - 1];
4534 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4535 // Safari & FF's stack traces don't contain error.message content
4536 // unlike those of Chrome and IE
4537 // So if stack doesn't contain message, we create a new string that contains both.
4538 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4540 e = e.message + '\n' + e.stack;
4542 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4543 module, e.stack || e.message || e);
4549 ////////////////////////////////////
4550 // internal Injector
4551 ////////////////////////////////////
4553 function createInternalInjector(cache, factory) {
4555 function getService(serviceName, caller) {
4556 if (cache.hasOwnProperty(serviceName)) {
4557 if (cache[serviceName] === INSTANTIATING) {
4558 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4559 serviceName + ' <- ' + path.join(' <- '));
4561 return cache[serviceName];
4564 path.unshift(serviceName);
4565 cache[serviceName] = INSTANTIATING;
4566 return cache[serviceName] = factory(serviceName, caller);
4568 if (cache[serviceName] === INSTANTIATING) {
4569 delete cache[serviceName];
4578 function invoke(fn, self, locals, serviceName) {
4579 if (typeof locals === 'string') {
4580 serviceName = locals;
4585 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4589 for (i = 0, length = $inject.length; i < length; i++) {
4591 if (typeof key !== 'string') {
4592 throw $injectorMinErr('itkn',
4593 'Incorrect injection token! Expected service name as string, got {0}', key);
4596 locals && locals.hasOwnProperty(key)
4598 : getService(key, serviceName)
4605 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4607 return fn.apply(self, args);
4610 function instantiate(Type, locals, serviceName) {
4611 // Check if Type is annotated and use just the given function at n-1 as parameter
4612 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4613 // Object creation: http://jsperf.com/create-constructor/2
4614 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4615 var returnedValue = invoke(Type, instance, locals, serviceName);
4617 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4622 instantiate: instantiate,
4624 annotate: createInjector.$$annotate,
4625 has: function(name) {
4626 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4632 createInjector.$$annotate = annotate;
4636 * @name $anchorScrollProvider
4639 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4640 * {@link ng.$location#hash $location.hash()} changes.
4642 function $AnchorScrollProvider() {
4644 var autoScrollingEnabled = true;
4648 * @name $anchorScrollProvider#disableAutoScrolling
4651 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4652 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4653 * Use this method to disable automatic scrolling.
4655 * If automatic scrolling is disabled, one must explicitly call
4656 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4659 this.disableAutoScrolling = function() {
4660 autoScrollingEnabled = false;
4665 * @name $anchorScroll
4668 * @requires $location
4669 * @requires $rootScope
4672 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4673 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4675 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4677 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4678 * match any anchor whenever it changes. This can be disabled by calling
4679 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4681 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4682 * vertical scroll-offset (either fixed or dynamic).
4684 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4685 * {@link ng.$location#hash $location.hash()} will be used.
4687 * @property {(number|function|jqLite)} yOffset
4688 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4689 * positioned elements at the top of the page, such as navbars, headers etc.
4691 * `yOffset` can be specified in various ways:
4692 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4693 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4694 * a number representing the offset (in pixels).<br /><br />
4695 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4696 * the top of the page to the element's bottom will be used as offset.<br />
4697 * **Note**: The element will be taken into account only as long as its `position` is set to
4698 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4699 * their height and/or positioning according to the viewport's size.
4702 * <div class="alert alert-warning">
4703 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4704 * not some child element.
4708 <example module="anchorScrollExample">
4709 <file name="index.html">
4710 <div id="scrollArea" ng-controller="ScrollController">
4711 <a ng-click="gotoBottom()">Go to bottom</a>
4712 <a id="bottom"></a> You're at the bottom!
4715 <file name="script.js">
4716 angular.module('anchorScrollExample', [])
4717 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4718 function ($scope, $location, $anchorScroll) {
4719 $scope.gotoBottom = function() {
4720 // set the location.hash to the id of
4721 // the element you wish to scroll to.
4722 $location.hash('bottom');
4724 // call $anchorScroll()
4729 <file name="style.css">
4743 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4744 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4747 <example module="anchorScrollOffsetExample">
4748 <file name="index.html">
4749 <div class="fixed-header" ng-controller="headerCtrl">
4750 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4754 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4758 <file name="script.js">
4759 angular.module('anchorScrollOffsetExample', [])
4760 .run(['$anchorScroll', function($anchorScroll) {
4761 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4763 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4764 function ($anchorScroll, $location, $scope) {
4765 $scope.gotoAnchor = function(x) {
4766 var newHash = 'anchor' + x;
4767 if ($location.hash() !== newHash) {
4768 // set the $location.hash to `newHash` and
4769 // $anchorScroll will automatically scroll to it
4770 $location.hash('anchor' + x);
4772 // call $anchorScroll() explicitly,
4773 // since $location.hash hasn't changed
4780 <file name="style.css">
4786 border: 2px dashed DarkOrchid;
4787 padding: 10px 10px 200px 10px;
4791 background-color: rgba(0, 0, 0, 0.2);
4794 top: 0; left: 0; right: 0;
4798 display: inline-block;
4804 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4805 var document = $window.document;
4807 // Helper function to get first anchor from a NodeList
4808 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4809 // and working in all supported browsers.)
4810 function getFirstAnchor(list) {
4812 Array.prototype.some.call(list, function(element) {
4813 if (nodeName_(element) === 'a') {
4821 function getYOffset() {
4823 var offset = scroll.yOffset;
4825 if (isFunction(offset)) {
4827 } else if (isElement(offset)) {
4828 var elem = offset[0];
4829 var style = $window.getComputedStyle(elem);
4830 if (style.position !== 'fixed') {
4833 offset = elem.getBoundingClientRect().bottom;
4835 } else if (!isNumber(offset)) {
4842 function scrollTo(elem) {
4844 elem.scrollIntoView();
4846 var offset = getYOffset();
4849 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4850 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4851 // top of the viewport.
4853 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4854 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4855 // way down the page.
4857 // This is often the case for elements near the bottom of the page.
4859 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4860 // the top of the element and the offset, which is enough to align the top of `elem` at the
4861 // desired position.
4862 var elemTop = elem.getBoundingClientRect().top;
4863 $window.scrollBy(0, elemTop - offset);
4866 $window.scrollTo(0, 0);
4870 function scroll(hash) {
4871 hash = isString(hash) ? hash : $location.hash();
4874 // empty hash, scroll to the top of the page
4875 if (!hash) scrollTo(null);
4877 // element with given id
4878 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4880 // first anchor with given name :-D
4881 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4883 // no element and hash == 'top', scroll to the top of the page
4884 else if (hash === 'top') scrollTo(null);
4887 // does not scroll when user clicks on anchor link that is currently on
4888 // (no url change, no $location.hash() change), browser native does scroll
4889 if (autoScrollingEnabled) {
4890 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4891 function autoScrollWatchAction(newVal, oldVal) {
4892 // skip the initial scroll if $location.hash is empty
4893 if (newVal === oldVal && newVal === '') return;
4895 jqLiteDocumentLoaded(function() {
4896 $rootScope.$evalAsync(scroll);
4905 var $animateMinErr = minErr('$animate');
4906 var ELEMENT_NODE = 1;
4907 var NG_ANIMATE_CLASSNAME = 'ng-animate';
4909 function mergeClasses(a,b) {
4910 if (!a && !b) return '';
4913 if (isArray(a)) a = a.join(' ');
4914 if (isArray(b)) b = b.join(' ');
4918 function extractElementNode(element) {
4919 for (var i = 0; i < element.length; i++) {
4920 var elm = element[i];
4921 if (elm.nodeType === ELEMENT_NODE) {
4927 function splitClasses(classes) {
4928 if (isString(classes)) {
4929 classes = classes.split(' ');
4932 // Use createMap() to prevent class assumptions involving property names in
4934 var obj = createMap();
4935 forEach(classes, function(klass) {
4936 // sometimes the split leaves empty string values
4937 // incase extra spaces were applied to the options
4945 // if any other type of options value besides an Object value is
4946 // passed into the $animate.method() animation then this helper code
4947 // will be run which will ignore it. While this patch is not the
4948 // greatest solution to this, a lot of existing plugins depend on
4949 // $animate to either call the callback (< 1.2) or return a promise
4950 // that can be changed. This helper function ensures that the options
4951 // are wiped clean incase a callback function is provided.
4952 function prepareAnimateOptions(options) {
4953 return isObject(options)
4958 var $$CoreAnimateRunnerProvider = function() {
4959 this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4960 function AnimateRunner() {}
4961 AnimateRunner.all = noop;
4962 AnimateRunner.chain = noop;
4963 AnimateRunner.prototype = {
4969 then: function(pass, fail) {
4970 return $q(function(resolve) {
4974 }).then(pass, fail);
4977 return AnimateRunner;
4981 // this is prefixed with Core since it conflicts with
4982 // the animateQueueProvider defined in ngAnimate/animateQueue.js
4983 var $$CoreAnimateQueueProvider = function() {
4984 var postDigestQueue = new HashMap();
4985 var postDigestElements = [];
4987 this.$get = ['$$AnimateRunner', '$rootScope',
4988 function($$AnimateRunner, $rootScope) {
4995 push: function(element, event, options, domOperation) {
4996 domOperation && domOperation();
4998 options = options || {};
4999 options.from && element.css(options.from);
5000 options.to && element.css(options.to);
5002 if (options.addClass || options.removeClass) {
5003 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5006 return new $$AnimateRunner(); // jshint ignore:line
5011 function updateData(data, classes, value) {
5012 var changed = false;
5014 classes = isString(classes) ? classes.split(' ') :
5015 isArray(classes) ? classes : [];
5016 forEach(classes, function(className) {
5019 data[className] = value;
5026 function handleCSSClassChanges() {
5027 forEach(postDigestElements, function(element) {
5028 var data = postDigestQueue.get(element);
5030 var existing = splitClasses(element.attr('class'));
5033 forEach(data, function(status, className) {
5034 var hasClass = !!existing[className];
5035 if (status !== hasClass) {
5037 toAdd += (toAdd.length ? ' ' : '') + className;
5039 toRemove += (toRemove.length ? ' ' : '') + className;
5044 forEach(element, function(elm) {
5045 toAdd && jqLiteAddClass(elm, toAdd);
5046 toRemove && jqLiteRemoveClass(elm, toRemove);
5048 postDigestQueue.remove(element);
5051 postDigestElements.length = 0;
5055 function addRemoveClassesPostDigest(element, add, remove) {
5056 var data = postDigestQueue.get(element) || {};
5058 var classesAdded = updateData(data, add, true);
5059 var classesRemoved = updateData(data, remove, false);
5061 if (classesAdded || classesRemoved) {
5063 postDigestQueue.put(element, data);
5064 postDigestElements.push(element);
5066 if (postDigestElements.length === 1) {
5067 $rootScope.$$postDigest(handleCSSClassChanges);
5076 * @name $animateProvider
5079 * Default implementation of $animate that doesn't perform any animations, instead just
5080 * synchronously performs DOM updates and resolves the returned runner promise.
5082 * In order to enable animations the `ngAnimate` module has to be loaded.
5084 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5086 var $AnimateProvider = ['$provide', function($provide) {
5087 var provider = this;
5089 this.$$registeredAnimations = Object.create(null);
5093 * @name $animateProvider#register
5096 * Registers a new injectable animation factory function. The factory function produces the
5097 * animation object which contains callback functions for each event that is expected to be
5100 * * `eventFn`: `function(element, ... , doneFunction, options)`
5101 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5102 * on the type of animation additional arguments will be injected into the animation function. The
5103 * list below explains the function signatures for the different animation methods:
5105 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5106 * - addClass: function(element, addedClasses, doneFunction, options)
5107 * - removeClass: function(element, removedClasses, doneFunction, options)
5108 * - enter, leave, move: function(element, doneFunction, options)
5109 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5111 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5115 * //enter, leave, move signature
5116 * eventFn : function(element, done, options) {
5117 * //code to run the animation
5118 * //once complete, then run done()
5119 * return function endFunction(wasCancelled) {
5120 * //code to cancel the animation
5126 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5127 * @param {Function} factory The factory function that will be executed to return the animation
5130 this.register = function(name, factory) {
5131 if (name && name.charAt(0) !== '.') {
5132 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5135 var key = name + '-animation';
5136 provider.$$registeredAnimations[name.substr(1)] = key;
5137 $provide.factory(key, factory);
5142 * @name $animateProvider#classNameFilter
5145 * Sets and/or returns the CSS class regular expression that is checked when performing
5146 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5147 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5148 * When setting the `classNameFilter` value, animations will only be performed on elements
5149 * that successfully match the filter expression. This in turn can boost performance
5150 * for low-powered devices as well as applications containing a lot of structural operations.
5151 * @param {RegExp=} expression The className expression which will be checked against all animations
5152 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5154 this.classNameFilter = function(expression) {
5155 if (arguments.length === 1) {
5156 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5157 if (this.$$classNameFilter) {
5158 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5159 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5160 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5165 return this.$$classNameFilter;
5168 this.$get = ['$$animateQueue', function($$animateQueue) {
5169 function domInsert(element, parentElement, afterElement) {
5170 // if for some reason the previous element was removed
5171 // from the dom sometime before this code runs then let's
5172 // just stick to using the parent element as the anchor
5174 var afterNode = extractElementNode(afterElement);
5175 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5176 afterElement = null;
5179 afterElement ? afterElement.after(element) : parentElement.prepend(element);
5185 * @description The $animate service exposes a series of DOM utility methods that provide support
5186 * for animation hooks. The default behavior is the application of DOM operations, however,
5187 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5188 * to ensure that animation runs with the triggered DOM operation.
5190 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5191 * included and only when it is active then the animation hooks that `$animate` triggers will be
5192 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5193 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5194 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5196 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5198 * To learn more about enabling animation support, click here to visit the
5199 * {@link ngAnimate ngAnimate module page}.
5202 // we don't call it directly since non-existant arguments may
5203 // be interpreted as null within the sub enabled function
5210 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5211 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5212 * is fired with the following params:
5215 * $animate.on('enter', container,
5216 * function callback(element, phase) {
5217 * // cool we detected an enter animation within the container
5222 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5223 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5224 * as well as among its children
5225 * @param {Function} callback the callback function that will be fired when the listener is triggered
5227 * The arguments present in the callback function are:
5228 * * `element` - The captured DOM element that the animation was fired on.
5229 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5231 on: $$animateQueue.on,
5236 * @name $animate#off
5238 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5239 * can be used in three different ways depending on the arguments:
5242 * // remove all the animation event listeners listening for `enter`
5243 * $animate.off('enter');
5245 * // remove all the animation event listeners listening for `enter` on the given element and its children
5246 * $animate.off('enter', container);
5248 * // remove the event listener function provided by `listenerFn` that is set
5249 * // to listen for `enter` on the given `element` as well as its children
5250 * $animate.off('enter', container, callback);
5253 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5254 * @param {DOMElement=} container the container element the event listener was placed on
5255 * @param {Function=} callback the callback function that was registered as the listener
5257 off: $$animateQueue.off,
5261 * @name $animate#pin
5263 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5264 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5265 * element despite being outside the realm of the application or within another application. Say for example if the application
5266 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5267 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5268 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5270 * Note that this feature is only active when the `ngAnimate` module is used.
5272 * @param {DOMElement} element the external element that will be pinned
5273 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5275 pin: $$animateQueue.pin,
5280 * @name $animate#enabled
5282 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5283 * function can be called in four ways:
5286 * // returns true or false
5287 * $animate.enabled();
5289 * // changes the enabled state for all animations
5290 * $animate.enabled(false);
5291 * $animate.enabled(true);
5293 * // returns true or false if animations are enabled for an element
5294 * $animate.enabled(element);
5296 * // changes the enabled state for an element and its children
5297 * $animate.enabled(element, true);
5298 * $animate.enabled(element, false);
5301 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5302 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5304 * @return {boolean} whether or not animations are enabled
5306 enabled: $$animateQueue.enabled,
5310 * @name $animate#cancel
5312 * @description Cancels the provided animation.
5314 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5316 cancel: function(runner) {
5317 runner.end && runner.end();
5323 * @name $animate#enter
5325 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5326 * as the first child within the `parent` element and then triggers an animation.
5327 * A promise is returned that will be resolved during the next digest once the animation
5330 * @param {DOMElement} element the element which will be inserted into the DOM
5331 * @param {DOMElement} parent the parent element which will append the element as
5332 * a child (so long as the after element is not present)
5333 * @param {DOMElement=} after the sibling element after which the element will be appended
5334 * @param {object=} options an optional collection of options/styles that will be applied to the element
5336 * @return {Promise} the animation callback promise
5338 enter: function(element, parent, after, options) {
5339 parent = parent && jqLite(parent);
5340 after = after && jqLite(after);
5341 parent = parent || after.parent();
5342 domInsert(element, parent, after);
5343 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5349 * @name $animate#move
5351 * @description Inserts (moves) the element into its new position in the DOM either after
5352 * the `after` element (if provided) or as the first child within the `parent` element
5353 * and then triggers an animation. A promise is returned that will be resolved
5354 * during the next digest once the animation has completed.
5356 * @param {DOMElement} element the element which will be moved into the new DOM position
5357 * @param {DOMElement} parent the parent element which will append the element as
5358 * a child (so long as the after element is not present)
5359 * @param {DOMElement=} after the sibling element after which the element will be appended
5360 * @param {object=} options an optional collection of options/styles that will be applied to the element
5362 * @return {Promise} the animation callback promise
5364 move: function(element, parent, after, options) {
5365 parent = parent && jqLite(parent);
5366 after = after && jqLite(after);
5367 parent = parent || after.parent();
5368 domInsert(element, parent, after);
5369 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5374 * @name $animate#leave
5376 * @description Triggers an animation and then removes the element from the DOM.
5377 * When the function is called a promise is returned that will be resolved during the next
5378 * digest once the animation has completed.
5380 * @param {DOMElement} element the element which will be removed from the DOM
5381 * @param {object=} options an optional collection of options/styles that will be applied to the element
5383 * @return {Promise} the animation callback promise
5385 leave: function(element, options) {
5386 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5393 * @name $animate#addClass
5396 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5397 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5398 * animation if element already contains the CSS class or if the class is removed at a later step.
5399 * Note that class-based animations are treated differently compared to structural animations
5400 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5401 * depending if CSS or JavaScript animations are used.
5403 * @param {DOMElement} element the element which the CSS classes will be applied to
5404 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5405 * @param {object=} options an optional collection of options/styles that will be applied to the element
5407 * @return {Promise} the animation callback promise
5409 addClass: function(element, className, options) {
5410 options = prepareAnimateOptions(options);
5411 options.addClass = mergeClasses(options.addclass, className);
5412 return $$animateQueue.push(element, 'addClass', options);
5417 * @name $animate#removeClass
5420 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5421 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5422 * animation if element does not contain the CSS class or if the class is added at a later step.
5423 * Note that class-based animations are treated differently compared to structural animations
5424 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5425 * depending if CSS or JavaScript animations are used.
5427 * @param {DOMElement} element the element which the CSS classes will be applied to
5428 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5429 * @param {object=} options an optional collection of options/styles that will be applied to the element
5431 * @return {Promise} the animation callback promise
5433 removeClass: function(element, className, options) {
5434 options = prepareAnimateOptions(options);
5435 options.removeClass = mergeClasses(options.removeClass, className);
5436 return $$animateQueue.push(element, 'removeClass', options);
5441 * @name $animate#setClass
5444 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5445 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5446 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5447 * passed. Note that class-based animations are treated differently compared to structural animations
5448 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5449 * depending if CSS or JavaScript animations are used.
5451 * @param {DOMElement} element the element which the CSS classes will be applied to
5452 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5453 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5454 * @param {object=} options an optional collection of options/styles that will be applied to the element
5456 * @return {Promise} the animation callback promise
5458 setClass: function(element, add, remove, options) {
5459 options = prepareAnimateOptions(options);
5460 options.addClass = mergeClasses(options.addClass, add);
5461 options.removeClass = mergeClasses(options.removeClass, remove);
5462 return $$animateQueue.push(element, 'setClass', options);
5467 * @name $animate#animate
5470 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5471 * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5472 * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5473 * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5474 * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5476 * @param {DOMElement} element the element which the CSS styles will be applied to
5477 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5478 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5479 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5480 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5481 * (Note that if no animation is detected then this value will not be appplied to the element.)
5482 * @param {object=} options an optional collection of options/styles that will be applied to the element
5484 * @return {Promise} the animation callback promise
5486 animate: function(element, from, to, className, options) {
5487 options = prepareAnimateOptions(options);
5488 options.from = options.from ? extend(options.from, from) : from;
5489 options.to = options.to ? extend(options.to, to) : to;
5491 className = className || 'ng-inline-animate';
5492 options.tempClasses = mergeClasses(options.tempClasses, className);
5493 return $$animateQueue.push(element, 'animate', options);
5505 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5506 * then the `$animateCss` service will actually perform animations.
5508 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5510 var $CoreAnimateCssProvider = function() {
5511 this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5513 var RAFPromise = function() {};
5514 RAFPromise.prototype = {
5515 done: function(cancel) {
5516 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5521 cancel: function() {
5524 getPromise: function() {
5526 this.defer = $q.defer();
5528 return this.defer.promise;
5530 then: function(f1,f2) {
5531 return this.getPromise().then(f1,f2);
5533 'catch': function(f1) {
5534 return this.getPromise()['catch'](f1);
5536 'finally': function(f1) {
5537 return this.getPromise()['finally'](f1);
5541 return function(element, options) {
5542 // there is no point in applying the styles since
5543 // there is no animation that goes on at all in
5544 // this version of $animateCss.
5545 if (options.cleanupStyles) {
5546 options.from = options.to = null;
5550 element.css(options.from);
5551 options.from = null;
5554 var closed, runner = new RAFPromise();
5572 if (options.addClass) {
5573 element.addClass(options.addClass);
5574 options.addClass = null;
5576 if (options.removeClass) {
5577 element.removeClass(options.removeClass);
5578 options.removeClass = null;
5581 element.css(options.to);
5589 /* global stripHash: true */
5592 * ! This is a private undocumented service !
5597 * This object has two goals:
5599 * - hide all the global state in the browser caused by the window object
5600 * - abstract away all the browser specific features and inconsistencies
5602 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5603 * service, which can be used for convenient testing of the application without the interaction with
5604 * the real browser apis.
5607 * @param {object} window The global window object.
5608 * @param {object} document jQuery wrapped document.
5609 * @param {object} $log window.console or an object with the same interface.
5610 * @param {object} $sniffer $sniffer service
5612 function Browser(window, document, $log, $sniffer) {
5614 rawDocument = document[0],
5615 location = window.location,
5616 history = window.history,
5617 setTimeout = window.setTimeout,
5618 clearTimeout = window.clearTimeout,
5619 pendingDeferIds = {};
5621 self.isMock = false;
5623 var outstandingRequestCount = 0;
5624 var outstandingRequestCallbacks = [];
5626 // TODO(vojta): remove this temporary api
5627 self.$$completeOutstandingRequest = completeOutstandingRequest;
5628 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5631 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5632 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5634 function completeOutstandingRequest(fn) {
5636 fn.apply(null, sliceArgs(arguments, 1));
5638 outstandingRequestCount--;
5639 if (outstandingRequestCount === 0) {
5640 while (outstandingRequestCallbacks.length) {
5642 outstandingRequestCallbacks.pop()();
5651 function getHash(url) {
5652 var index = url.indexOf('#');
5653 return index === -1 ? '' : url.substr(index);
5658 * Note: this method is used only by scenario runner
5659 * TODO(vojta): prefix this method with $$ ?
5660 * @param {function()} callback Function that will be called when no outstanding request
5662 self.notifyWhenNoOutstandingRequests = function(callback) {
5663 if (outstandingRequestCount === 0) {
5666 outstandingRequestCallbacks.push(callback);
5670 //////////////////////////////////////////////////////////////
5672 //////////////////////////////////////////////////////////////
5674 var cachedState, lastHistoryState,
5675 lastBrowserUrl = location.href,
5676 baseElement = document.find('base'),
5677 pendingLocation = null;
5680 lastHistoryState = cachedState;
5683 * @name $browser#url
5687 * Without any argument, this method just returns current value of location.href.
5690 * With at least one argument, this method sets url to new value.
5691 * If html5 history api supported, pushState/replaceState is used, otherwise
5692 * location.href/location.replace is used.
5693 * Returns its own instance to allow chaining
5695 * NOTE: this api is intended for use only by the $location service. Please use the
5696 * {@link ng.$location $location service} to change url.
5698 * @param {string} url New url (when used as setter)
5699 * @param {boolean=} replace Should new url replace current history record?
5700 * @param {object=} state object to use with pushState/replaceState
5702 self.url = function(url, replace, state) {
5703 // In modern browsers `history.state` is `null` by default; treating it separately
5704 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5705 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5706 if (isUndefined(state)) {
5710 // Android Browser BFCache causes location, history reference to become stale.
5711 if (location !== window.location) location = window.location;
5712 if (history !== window.history) history = window.history;
5716 var sameState = lastHistoryState === state;
5718 // Don't change anything if previous and current URLs and states match. This also prevents
5719 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5720 // See https://github.com/angular/angular.js/commit/ffb2701
5721 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5724 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5725 lastBrowserUrl = url;
5726 lastHistoryState = state;
5727 // Don't use history API if only the hash changed
5728 // due to a bug in IE10/IE11 which leads
5729 // to not firing a `hashchange` nor `popstate` event
5730 // in some cases (see #9143).
5731 if ($sniffer.history && (!sameBase || !sameState)) {
5732 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5734 // Do the assignment again so that those two variables are referentially identical.
5735 lastHistoryState = cachedState;
5737 if (!sameBase || pendingLocation) {
5738 pendingLocation = url;
5741 location.replace(url);
5742 } else if (!sameBase) {
5743 location.href = url;
5745 location.hash = getHash(url);
5747 if (location.href !== url) {
5748 pendingLocation = url;
5754 // - pendingLocation is needed as browsers don't allow to read out
5755 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5756 // https://openradar.appspot.com/22186109).
5757 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5758 return pendingLocation || location.href.replace(/%27/g,"'");
5763 * @name $browser#state
5766 * This method is a getter.
5768 * Return history.state or null if history.state is undefined.
5770 * @returns {object} state
5772 self.state = function() {
5776 var urlChangeListeners = [],
5777 urlChangeInit = false;
5779 function cacheStateAndFireUrlChange() {
5780 pendingLocation = null;
5785 function getCurrentState() {
5787 return history.state;
5789 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5793 // This variable should be used *only* inside the cacheState function.
5794 var lastCachedState = null;
5795 function cacheState() {
5796 // This should be the only place in $browser where `history.state` is read.
5797 cachedState = getCurrentState();
5798 cachedState = isUndefined(cachedState) ? null : cachedState;
5800 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5801 if (equals(cachedState, lastCachedState)) {
5802 cachedState = lastCachedState;
5804 lastCachedState = cachedState;
5807 function fireUrlChange() {
5808 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5812 lastBrowserUrl = self.url();
5813 lastHistoryState = cachedState;
5814 forEach(urlChangeListeners, function(listener) {
5815 listener(self.url(), cachedState);
5820 * @name $browser#onUrlChange
5823 * Register callback function that will be called, when url changes.
5825 * It's only called when the url is changed from outside of angular:
5826 * - user types different url into address bar
5827 * - user clicks on history (forward/back) button
5828 * - user clicks on a link
5830 * It's not called when url is changed by $browser.url() method
5832 * The listener gets called with new url as parameter.
5834 * NOTE: this api is intended for use only by the $location service. Please use the
5835 * {@link ng.$location $location service} to monitor url changes in angular apps.
5837 * @param {function(string)} listener Listener function to be called when url changes.
5838 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5840 self.onUrlChange = function(callback) {
5841 // TODO(vojta): refactor to use node's syntax for events
5842 if (!urlChangeInit) {
5843 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5844 // don't fire popstate when user change the address bar and don't fire hashchange when url
5845 // changed by push/replaceState
5847 // html5 history api - popstate event
5848 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5850 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5852 urlChangeInit = true;
5855 urlChangeListeners.push(callback);
5861 * Remove popstate and hashchange handler from window.
5863 * NOTE: this api is intended for use only by $rootScope.
5865 self.$$applicationDestroyed = function() {
5866 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5870 * Checks whether the url has changed outside of Angular.
5871 * Needs to be exported to be able to check for changes that have been done in sync,
5872 * as hashchange/popstate events fire in async.
5874 self.$$checkUrlChange = fireUrlChange;
5876 //////////////////////////////////////////////////////////////
5878 //////////////////////////////////////////////////////////////
5881 * @name $browser#baseHref
5884 * Returns current <base href>
5885 * (always relative - without domain)
5887 * @returns {string} The current base href
5889 self.baseHref = function() {
5890 var href = baseElement.attr('href');
5891 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5895 * @name $browser#defer
5896 * @param {function()} fn A function, who's execution should be deferred.
5897 * @param {number=} [delay=0] of milliseconds to defer the function execution.
5898 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5901 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5903 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5904 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5905 * via `$browser.defer.flush()`.
5908 self.defer = function(fn, delay) {
5910 outstandingRequestCount++;
5911 timeoutId = setTimeout(function() {
5912 delete pendingDeferIds[timeoutId];
5913 completeOutstandingRequest(fn);
5915 pendingDeferIds[timeoutId] = true;
5921 * @name $browser#defer.cancel
5924 * Cancels a deferred task identified with `deferId`.
5926 * @param {*} deferId Token returned by the `$browser.defer` function.
5927 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5930 self.defer.cancel = function(deferId) {
5931 if (pendingDeferIds[deferId]) {
5932 delete pendingDeferIds[deferId];
5933 clearTimeout(deferId);
5934 completeOutstandingRequest(noop);
5942 function $BrowserProvider() {
5943 this.$get = ['$window', '$log', '$sniffer', '$document',
5944 function($window, $log, $sniffer, $document) {
5945 return new Browser($window, $document, $log, $sniffer);
5951 * @name $cacheFactory
5954 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5959 * var cache = $cacheFactory('cacheId');
5960 * expect($cacheFactory.get('cacheId')).toBe(cache);
5961 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5963 * cache.put("key", "value");
5964 * cache.put("another key", "another value");
5966 * // We've specified no options on creation
5967 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5972 * @param {string} cacheId Name or id of the newly created cache.
5973 * @param {object=} options Options object that specifies the cache behavior. Properties:
5975 * - `{number=}` `capacity` — turns the cache into LRU cache.
5977 * @returns {object} Newly created cache object with the following set of methods:
5979 * - `{object}` `info()` — Returns id, size, and options of cache.
5980 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5982 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5983 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5984 * - `{void}` `removeAll()` — Removes all cached values.
5985 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5988 <example module="cacheExampleApp">
5989 <file name="index.html">
5990 <div ng-controller="CacheController">
5991 <input ng-model="newCacheKey" placeholder="Key">
5992 <input ng-model="newCacheValue" placeholder="Value">
5993 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5995 <p ng-if="keys.length">Cached Values</p>
5996 <div ng-repeat="key in keys">
5997 <span ng-bind="key"></span>
5999 <b ng-bind="cache.get(key)"></b>
6003 <div ng-repeat="(key, value) in cache.info()">
6004 <span ng-bind="key"></span>
6006 <b ng-bind="value"></b>
6010 <file name="script.js">
6011 angular.module('cacheExampleApp', []).
6012 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6014 $scope.cache = $cacheFactory('cacheId');
6015 $scope.put = function(key, value) {
6016 if (angular.isUndefined($scope.cache.get(key))) {
6017 $scope.keys.push(key);
6019 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6023 <file name="style.css">
6030 function $CacheFactoryProvider() {
6032 this.$get = function() {
6035 function cacheFactory(cacheId, options) {
6036 if (cacheId in caches) {
6037 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6041 stats = extend({}, options, {id: cacheId}),
6043 capacity = (options && options.capacity) || Number.MAX_VALUE,
6044 lruHash = createMap(),
6050 * @name $cacheFactory.Cache
6053 * A cache object used to store and retrieve data, primarily used by
6054 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6055 * templates and other data.
6058 * angular.module('superCache')
6059 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6060 * return $cacheFactory('super-cache');
6067 * it('should behave like a cache', inject(function(superCache) {
6068 * superCache.put('key', 'value');
6069 * superCache.put('another key', 'another value');
6071 * expect(superCache.info()).toEqual({
6072 * id: 'super-cache',
6076 * superCache.remove('another key');
6077 * expect(superCache.get('another key')).toBeUndefined();
6079 * superCache.removeAll();
6080 * expect(superCache.info()).toEqual({
6081 * id: 'super-cache',
6087 return caches[cacheId] = {
6091 * @name $cacheFactory.Cache#put
6095 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6096 * retrieved later, and incrementing the size of the cache if the key was not already
6097 * present in the cache. If behaving like an LRU cache, it will also remove stale
6098 * entries from the set.
6100 * It will not insert undefined values into the cache.
6102 * @param {string} key the key under which the cached data is stored.
6103 * @param {*} value the value to store alongside the key. If it is undefined, the key
6104 * will not be stored.
6105 * @returns {*} the value stored.
6107 put: function(key, value) {
6108 if (isUndefined(value)) return;
6109 if (capacity < Number.MAX_VALUE) {
6110 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6115 if (!(key in data)) size++;
6118 if (size > capacity) {
6119 this.remove(staleEnd.key);
6127 * @name $cacheFactory.Cache#get
6131 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6133 * @param {string} key the key of the data to be retrieved
6134 * @returns {*} the value stored.
6136 get: function(key) {
6137 if (capacity < Number.MAX_VALUE) {
6138 var lruEntry = lruHash[key];
6140 if (!lruEntry) return;
6151 * @name $cacheFactory.Cache#remove
6155 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6157 * @param {string} key the key of the entry to be removed
6159 remove: function(key) {
6160 if (capacity < Number.MAX_VALUE) {
6161 var lruEntry = lruHash[key];
6163 if (!lruEntry) return;
6165 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6166 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6167 link(lruEntry.n,lruEntry.p);
6169 delete lruHash[key];
6172 if (!(key in data)) return;
6181 * @name $cacheFactory.Cache#removeAll
6185 * Clears the cache object of any entries.
6187 removeAll: function() {
6190 lruHash = createMap();
6191 freshEnd = staleEnd = null;
6197 * @name $cacheFactory.Cache#destroy
6201 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6202 * removing it from the {@link $cacheFactory $cacheFactory} set.
6204 destroy: function() {
6208 delete caches[cacheId];
6214 * @name $cacheFactory.Cache#info
6218 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6220 * @returns {object} an object with the following properties:
6222 * <li>**id**: the id of the cache instance</li>
6223 * <li>**size**: the number of entries kept in the cache instance</li>
6224 * <li>**...**: any additional properties from the options object when creating the
6229 return extend({}, stats, {size: size});
6235 * makes the `entry` the freshEnd of the LRU linked list
6237 function refresh(entry) {
6238 if (entry != freshEnd) {
6241 } else if (staleEnd == entry) {
6245 link(entry.n, entry.p);
6246 link(entry, freshEnd);
6254 * bidirectionally links two entries of the LRU linked list
6256 function link(nextEntry, prevEntry) {
6257 if (nextEntry != prevEntry) {
6258 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6259 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6267 * @name $cacheFactory#info
6270 * Get information about all the caches that have been created
6272 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6274 cacheFactory.info = function() {
6276 forEach(caches, function(cache, cacheId) {
6277 info[cacheId] = cache.info();
6285 * @name $cacheFactory#get
6288 * Get access to a cache object by the `cacheId` used when it was created.
6290 * @param {string} cacheId Name or id of a cache to access.
6291 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6293 cacheFactory.get = function(cacheId) {
6294 return caches[cacheId];
6298 return cacheFactory;
6304 * @name $templateCache
6307 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6308 * can load templates directly into the cache in a `script` tag, or by consuming the
6309 * `$templateCache` service directly.
6311 * Adding via the `script` tag:
6314 * <script type="text/ng-template" id="templateId.html">
6315 * <p>This is the content of the template</p>
6319 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6320 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6321 * element with ng-app attribute), otherwise the template will be ignored.
6323 * Adding via the `$templateCache` service:
6326 * var myApp = angular.module('myApp', []);
6327 * myApp.run(function($templateCache) {
6328 * $templateCache.put('templateId.html', 'This is the content of the template');
6332 * To retrieve the template later, simply use it in your HTML:
6334 * <div ng-include=" 'templateId.html' "></div>
6337 * or get it via Javascript:
6339 * $templateCache.get('templateId.html')
6342 * See {@link ng.$cacheFactory $cacheFactory}.
6345 function $TemplateCacheProvider() {
6346 this.$get = ['$cacheFactory', function($cacheFactory) {
6347 return $cacheFactory('templates');
6351 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6352 * Any commits to this file should be reviewed with security in mind. *
6353 * Changes to this file can potentially create security vulnerabilities. *
6354 * An approval from 2 Core members with history of modifying *
6355 * this file is required. *
6357 * Does the change somehow allow for arbitrary javascript to be executed? *
6358 * Or allows for someone to change the prototype of built-in objects? *
6359 * Or gives undesired access to variables likes document or window? *
6360 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6362 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6364 * DOM-related variables:
6366 * - "node" - DOM Node
6367 * - "element" - DOM Element or Node
6368 * - "$node" or "$element" - jqLite-wrapped node or element
6371 * Compiler related stuff:
6373 * - "linkFn" - linking fn of a single directive
6374 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6375 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
6376 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6386 * Compiles an HTML string or DOM into a template and produces a template function, which
6387 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6389 * The compilation is a process of walking the DOM tree and matching DOM elements to
6390 * {@link ng.$compileProvider#directive directives}.
6392 * <div class="alert alert-warning">
6393 * **Note:** This document is an in-depth reference of all directive options.
6394 * For a gentle introduction to directives with examples of common use cases,
6395 * see the {@link guide/directive directive guide}.
6398 * ## Comprehensive Directive API
6400 * There are many different options for a directive.
6402 * The difference resides in the return value of the factory function.
6403 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6404 * or just the `postLink` function (all other properties will have the default values).
6406 * <div class="alert alert-success">
6407 * **Best Practice:** It's recommended to use the "directive definition object" form.
6410 * Here's an example directive declared with a Directive Definition Object:
6413 * var myModule = angular.module(...);
6415 * myModule.directive('directiveName', function factory(injectables) {
6416 * var directiveDefinitionObject = {
6418 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6420 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6421 * transclude: false,
6423 * templateNamespace: 'html',
6425 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6426 * controllerAs: 'stringIdentifier',
6427 * bindToController: false,
6428 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6429 * compile: function compile(tElement, tAttrs, transclude) {
6431 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6432 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
6435 * // return function postLink( ... ) { ... }
6439 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6440 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
6443 * // link: function postLink( ... ) { ... }
6445 * return directiveDefinitionObject;
6449 * <div class="alert alert-warning">
6450 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6453 * Therefore the above can be simplified as:
6456 * var myModule = angular.module(...);
6458 * myModule.directive('directiveName', function factory(injectables) {
6459 * var directiveDefinitionObject = {
6460 * link: function postLink(scope, iElement, iAttrs) { ... }
6462 * return directiveDefinitionObject;
6464 * // return function postLink(scope, iElement, iAttrs) { ... }
6470 * ### Directive Definition Object
6472 * The directive definition object provides instructions to the {@link ng.$compile
6473 * compiler}. The attributes are:
6475 * #### `multiElement`
6476 * When this property is set to true, the HTML compiler will collect DOM nodes between
6477 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6478 * together as the directive elements. It is recommended that this feature be used on directives
6479 * which are not strictly behavioural (such as {@link ngClick}), and which
6480 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6483 * When there are multiple directives defined on a single DOM element, sometimes it
6484 * is necessary to specify the order in which the directives are applied. The `priority` is used
6485 * to sort the directives before their `compile` functions get called. Priority is defined as a
6486 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6487 * are also run in priority order, but post-link functions are run in reverse order. The order
6488 * of directives with the same priority is undefined. The default priority is `0`.
6491 * If set to true then the current `priority` will be the last set of directives
6492 * which will execute (any directives at the current priority will still execute
6493 * as the order of execution on same `priority` is undefined). Note that expressions
6494 * and other directives used in the directive's template will also be excluded from execution.
6497 * The scope property can be `true`, an object or a falsy value:
6499 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6501 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6502 * the directive's element. If multiple directives on the same element request a new scope,
6503 * only one new scope is created. The new scope rule does not apply for the root of the template
6504 * since the root of the template always gets a new scope.
6506 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6507 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6508 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6509 * data in the parent scope.
6511 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6512 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6513 * the object hash map to the name of the property on the isolate scope; the values define how the property
6514 * is bound to the parent scope, via matching attributes on the directive's element:
6516 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6517 * always a string since DOM attributes are strings. If no `attr` name is specified then the
6518 * attribute name is assumed to be the same as the local name.
6519 * Given `<widget my-attr="hello {{name}}">` and widget definition
6520 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6521 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6522 * `localName` property on the widget scope. The `name` is read from the parent scope (not
6525 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6526 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6527 * name is specified then the attribute name is assumed to be the same as the local name.
6528 * Given `<widget my-attr="parentModel">` and widget definition of
6529 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6530 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6531 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6532 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6533 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6534 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6535 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6537 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6538 * If no `attr` name is specified then the attribute name is assumed to be the same as the
6539 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
6540 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6541 * a function wrapper for the `count = count + value` expression. Often it's desirable to
6542 * pass data from the isolated scope via an expression to the parent scope, this can be
6543 * done by passing a map of local variable names and values into the expression wrapper fn.
6544 * For example, if the expression is `increment(amount)` then we can specify the amount value
6545 * by calling the `localFn` as `localFn({amount: 22})`.
6547 * In general it's possible to apply more than one directive to one element, but there might be limitations
6548 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6549 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6551 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6552 * * **child scope** + **no scope** => Both directives will share one single child scope
6553 * * **child scope** + **child scope** => Both directives will share one single child scope
6554 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6555 * its parent's scope
6556 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6557 * be applied to the same element.
6558 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6559 * cannot be applied to the same element.
6562 * #### `bindToController`
6563 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6564 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6565 * is instantiated, the initial values of the isolate scope bindings are already available.
6568 * Controller constructor function. The controller is instantiated before the
6569 * pre-linking phase and can be accessed by other directives (see
6570 * `require` attribute). This allows the directives to communicate with each other and augment
6571 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6573 * * `$scope` - Current scope associated with the element
6574 * * `$element` - Current element
6575 * * `$attrs` - Current attributes object for the element
6576 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6577 * `function([scope], cloneLinkingFn, futureParentElement)`.
6578 * * `scope`: optional argument to override the scope.
6579 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6580 * * `futureParentElement`:
6581 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6582 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6583 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6584 * and when the `cloneLinkinFn` is passed,
6585 * as those elements need to created and cloned in a special way when they are defined outside their
6586 * usual containers (e.g. like `<svg>`).
6587 * * See also the `directive.templateNamespace` property.
6591 * Require another directive and inject its controller as the fourth argument to the linking function. The
6592 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6593 * injected argument will be an array in corresponding order. If no such directive can be
6594 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6595 * is specified, in which case error checking is skipped). The name can be prefixed with:
6597 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6598 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6599 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6600 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6601 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6602 * `null` to the `link` fn if not found.
6603 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6604 * `null` to the `link` fn if not found.
6607 * #### `controllerAs`
6608 * Identifier name for a reference to the controller in the directive's scope.
6609 * This allows the controller to be referenced from the directive template. This is especially
6610 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6611 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6612 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6616 * String of subset of `EACM` which restricts the directive to a specific directive
6617 * declaration style. If omitted, the defaults (elements and attributes) are used.
6619 * * `E` - Element name (default): `<my-directive></my-directive>`
6620 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6621 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6622 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6625 * #### `templateNamespace`
6626 * String representing the document type used by the markup in the template.
6627 * AngularJS needs this information as those elements need to be created and cloned
6628 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6630 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6631 * top-level elements such as `<svg>` or `<math>`.
6632 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6633 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6635 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6638 * HTML markup that may:
6639 * * Replace the contents of the directive's element (default).
6640 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6641 * * Wrap the contents of the directive's element (if `transclude` is true).
6645 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6646 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6647 * function api below) and returns a string value.
6650 * #### `templateUrl`
6651 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6653 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6654 * for later when the template has been resolved. In the meantime it will continue to compile and link
6655 * sibling and parent elements as though this element had not contained any directives.
6657 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6658 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6659 * case when only one deeply nested directive has `templateUrl`.
6661 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6663 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6664 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6665 * a string value representing the url. In either case, the template URL is passed through {@link
6666 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6669 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6670 * specify what the template should replace. Defaults to `false`.
6672 * * `true` - the template will replace the directive's element.
6673 * * `false` - the template will replace the contents of the directive's element.
6675 * The replacement process migrates all of the attributes / classes from the old element to the new
6676 * one. See the {@link guide/directive#template-expanding-directive
6677 * Directives Guide} for an example.
6679 * There are very few scenarios where element replacement is required for the application function,
6680 * the main one being reusable custom components that are used within SVG contexts
6681 * (because SVG doesn't work with custom elements in the DOM tree).
6684 * Extract the contents of the element where the directive appears and make it available to the directive.
6685 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6686 * {@link $compile#transclusion Transclusion} section below.
6688 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6689 * directive's element or the entire element:
6691 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6692 * * `'element'` - transclude the whole of the directive's element including any directives on this
6693 * element that defined at a lower priority than this directive. When used, the `template`
6694 * property is ignored.
6700 * function compile(tElement, tAttrs, transclude) { ... }
6703 * The compile function deals with transforming the template DOM. Since most directives do not do
6704 * template transformation, it is not used often. The compile function takes the following arguments:
6706 * * `tElement` - template element - The element where the directive has been declared. It is
6707 * safe to do template transformation on the element and child elements only.
6709 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6710 * between all directive compile functions.
6712 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6714 * <div class="alert alert-warning">
6715 * **Note:** The template instance and the link instance may be different objects if the template has
6716 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6717 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6718 * should be done in a linking function rather than in a compile function.
6721 * <div class="alert alert-warning">
6722 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6723 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6724 * stack overflow errors.
6726 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6727 * a directive's template instead of relying on automatic template compilation via `template` or
6728 * `templateUrl` declaration or manual compilation inside the compile function.
6731 * <div class="alert alert-danger">
6732 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6733 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6734 * to the link function instead.
6737 * A compile function can have a return value which can be either a function or an object.
6739 * * returning a (post-link) function - is equivalent to registering the linking function via the
6740 * `link` property of the config object when the compile function is empty.
6742 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6743 * control when a linking function should be called during the linking phase. See info about
6744 * pre-linking and post-linking functions below.
6748 * This property is used only if the `compile` property is not defined.
6751 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6754 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6755 * executed after the template has been cloned. This is where most of the directive logic will be
6758 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6759 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6761 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6762 * manipulate the children of the element only in `postLink` function since the children have
6763 * already been linked.
6765 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6766 * between all directive linking functions.
6768 * * `controller` - the directive's required controller instance(s) - Instances are shared
6769 * among all directives, which allows the directives to use the controllers as a communication
6770 * channel. The exact value depends on the directive's `require` property:
6771 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6772 * * `string`: the controller instance
6773 * * `array`: array of controller instances
6775 * If a required controller cannot be found, and it is optional, the instance is `null`,
6776 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6778 * Note that you can also require the directive's own controller - it will be made available like
6779 * any other controller.
6781 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6782 * This is the same as the `$transclude`
6783 * parameter of directive controllers, see there for details.
6784 * `function([scope], cloneLinkingFn, futureParentElement)`.
6786 * #### Pre-linking function
6788 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6789 * compiler linking function will fail to locate the correct elements for linking.
6791 * #### Post-linking function
6793 * Executed after the child elements are linked.
6795 * Note that child elements that contain `templateUrl` directives will not have been compiled
6796 * and linked since they are waiting for their template to load asynchronously and their own
6797 * compilation and linking has been suspended until that occurs.
6799 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6800 * for their async templates to be resolved.
6805 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6806 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6807 * scope from where they were taken.
6809 * Transclusion is used (often with {@link ngTransclude}) to insert the
6810 * original contents of a directive's element into a specified place in the template of the directive.
6811 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6812 * content has access to the properties on the scope from which it was taken, even if the directive
6813 * has isolated scope.
6814 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6816 * This makes it possible for the widget to have private state for its template, while the transcluded
6817 * content has access to its originating scope.
6819 * <div class="alert alert-warning">
6820 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6821 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6822 * Testing Transclusion Directives}.
6825 * #### Transclusion Functions
6827 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6828 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6829 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6831 * <div class="alert alert-info">
6832 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6833 * ngTransclude will deal with it for us.
6836 * If you want to manually control the insertion and removal of the transcluded content in your directive
6837 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6838 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6840 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6841 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6842 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6844 * <div class="alert alert-info">
6845 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6846 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6849 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6850 * attach function**:
6853 * var transcludedContent, transclusionScope;
6855 * $transclude(function(clone, scope) {
6856 * element.append(clone);
6857 * transcludedContent = clone;
6858 * transclusionScope = scope;
6862 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6863 * associated transclusion scope:
6866 * transcludedContent.remove();
6867 * transclusionScope.$destroy();
6870 * <div class="alert alert-info">
6871 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6872 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6873 * then you are also responsible for calling `$destroy` on the transclusion scope.
6876 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6877 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6878 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6881 * #### Transclusion Scopes
6883 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6884 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6885 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6888 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6894 * <div transclusion>
6900 * The `$parent` scope hierarchy will look like this:
6908 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6919 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6920 * `link()` or `compile()` functions. It has a variety of uses.
6922 * accessing *Normalized attribute names:*
6923 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6924 * the attributes object allows for normalized access to
6927 * * *Directive inter-communication:* All directives share the same instance of the attributes
6928 * object which allows the directives to use the attributes object as inter directive
6931 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6932 * allowing other directives to read the interpolated value.
6934 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6935 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6936 * the only way to easily get the actual value because during the linking phase the interpolation
6937 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
6940 * function linkingFn(scope, elm, attrs, ctrl) {
6941 * // get the attribute value
6942 * console.log(attrs.ngModel);
6944 * // change the attribute
6945 * attrs.$set('ngModel', 'new value');
6947 * // observe changes to interpolated attribute
6948 * attrs.$observe('ngModel', function(value) {
6949 * console.log('ngModel has changed value to ' + value);
6956 * <div class="alert alert-warning">
6957 * **Note**: Typically directives are registered with `module.directive`. The example below is
6958 * to illustrate how `$compile` works.
6961 <example module="compileExample">
6962 <file name="index.html">
6964 angular.module('compileExample', [], function($compileProvider) {
6965 // configure new 'compile' directive by passing a directive
6966 // factory function. The factory function injects the '$compile'
6967 $compileProvider.directive('compile', function($compile) {
6968 // directive factory creates a link function
6969 return function(scope, element, attrs) {
6972 // watch the 'compile' expression for changes
6973 return scope.$eval(attrs.compile);
6976 // when the 'compile' expression changes
6977 // assign it into the current DOM
6978 element.html(value);
6980 // compile the new DOM and link it to the current
6982 // NOTE: we only compile .childNodes so that
6983 // we don't get into infinite loop compiling ourselves
6984 $compile(element.contents())(scope);
6990 .controller('GreeterController', ['$scope', function($scope) {
6991 $scope.name = 'Angular';
6992 $scope.html = 'Hello {{name}}';
6995 <div ng-controller="GreeterController">
6996 <input ng-model="name"> <br/>
6997 <textarea ng-model="html"></textarea> <br/>
6998 <div compile="html"></div>
7001 <file name="protractor.js" type="protractor">
7002 it('should auto compile', function() {
7003 var textarea = $('textarea');
7004 var output = $('div[compile]');
7005 // The initial state reads 'Hello Angular'.
7006 expect(output.getText()).toBe('Hello Angular');
7008 textarea.sendKeys('{{name}}!');
7009 expect(output.getText()).toBe('Angular!');
7016 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7017 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7019 * <div class="alert alert-danger">
7020 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7021 * e.g. will not use the right outer scope. Please pass the transclude function as a
7022 * `parentBoundTranscludeFn` to the link function instead.
7025 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7026 * root element(s), not their children)
7027 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7028 * (a DOM element/tree) to a scope. Where:
7030 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7031 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7032 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
7033 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7034 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7036 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
7037 * * `scope` - is the current scope with which the linking function is working with.
7039 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
7040 * keys may be used to control linking behavior:
7042 * * `parentBoundTranscludeFn` - the transclude function made available to
7043 * directives; if given, it will be passed through to the link functions of
7044 * directives found in `element` during compilation.
7045 * * `transcludeControllers` - an object hash with keys that map controller names
7046 * to controller instances; if given, it will make the controllers
7047 * available to directives.
7048 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7049 * the cloned elements; only needed for transcludes that are allowed to contain non html
7050 * elements (e.g. SVG elements). See also the directive.controller property.
7052 * Calling the linking function returns the element of the template. It is either the original
7053 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7055 * After linking the view is not updated until after a call to $digest which typically is done by
7056 * Angular automatically.
7058 * If you need access to the bound view, there are two ways to do it:
7060 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7061 * before you send them to the compiler and keep this reference around.
7063 * var element = $compile('<p>{{total}}</p>')(scope);
7066 * - if on the other hand, you need the element to be cloned, the view reference from the original
7067 * example would not point to the clone, but rather to the original template that was cloned. In
7068 * this case, you can access the clone via the cloneAttachFn:
7070 * var templateElement = angular.element('<p>{{total}}</p>'),
7073 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7074 * //attach the clone to DOM document at the right place
7077 * //now we have reference to the cloned DOM via `clonedElement`
7081 * For information on how the compiler works, see the
7082 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7085 var $compileMinErr = minErr('$compile');
7089 * @name $compileProvider
7093 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7094 function $CompileProvider($provide, $$sanitizeUriProvider) {
7095 var hasDirectives = {},
7096 Suffix = 'Directive',
7097 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7098 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7099 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7100 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7102 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7103 // The assumption is that future DOM event attribute names will begin with
7104 // 'on' and be composed of only English letters.
7105 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7107 function parseIsolateBindings(scope, directiveName, isController) {
7108 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7112 forEach(scope, function(definition, scopeName) {
7113 var match = definition.match(LOCAL_REGEXP);
7116 throw $compileMinErr('iscp',
7117 "Invalid {3} for directive '{0}'." +
7118 " Definition: {... {1}: '{2}' ...}",
7119 directiveName, scopeName, definition,
7120 (isController ? "controller bindings definition" :
7121 "isolate scope definition"));
7124 bindings[scopeName] = {
7126 collection: match[2] === '*',
7127 optional: match[3] === '?',
7128 attrName: match[4] || scopeName
7135 function parseDirectiveBindings(directive, directiveName) {
7138 bindToController: null
7140 if (isObject(directive.scope)) {
7141 if (directive.bindToController === true) {
7142 bindings.bindToController = parseIsolateBindings(directive.scope,
7143 directiveName, true);
7144 bindings.isolateScope = {};
7146 bindings.isolateScope = parseIsolateBindings(directive.scope,
7147 directiveName, false);
7150 if (isObject(directive.bindToController)) {
7151 bindings.bindToController =
7152 parseIsolateBindings(directive.bindToController, directiveName, true);
7154 if (isObject(bindings.bindToController)) {
7155 var controller = directive.controller;
7156 var controllerAs = directive.controllerAs;
7158 // There is no controller, there may or may not be a controllerAs property
7159 throw $compileMinErr('noctrl',
7160 "Cannot bind to controller without directive '{0}'s controller.",
7162 } else if (!identifierForController(controller, controllerAs)) {
7163 // There is a controller, but no identifier or controllerAs property
7164 throw $compileMinErr('noident',
7165 "Cannot bind to controller without identifier for directive '{0}'.",
7172 function assertValidDirectiveName(name) {
7173 var letter = name.charAt(0);
7174 if (!letter || letter !== lowercase(letter)) {
7175 throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7177 if (name !== name.trim()) {
7178 throw $compileMinErr('baddir',
7179 "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7186 * @name $compileProvider#directive
7190 * Register a new directive with the compiler.
7192 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7193 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7194 * names and the values are the factories.
7195 * @param {Function|Array} directiveFactory An injectable directive factory function. See
7196 * {@link guide/directive} for more info.
7197 * @returns {ng.$compileProvider} Self for chaining.
7199 this.directive = function registerDirective(name, directiveFactory) {
7200 assertNotHasOwnProperty(name, 'directive');
7201 if (isString(name)) {
7202 assertValidDirectiveName(name);
7203 assertArg(directiveFactory, 'directiveFactory');
7204 if (!hasDirectives.hasOwnProperty(name)) {
7205 hasDirectives[name] = [];
7206 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7207 function($injector, $exceptionHandler) {
7208 var directives = [];
7209 forEach(hasDirectives[name], function(directiveFactory, index) {
7211 var directive = $injector.invoke(directiveFactory);
7212 if (isFunction(directive)) {
7213 directive = { compile: valueFn(directive) };
7214 } else if (!directive.compile && directive.link) {
7215 directive.compile = valueFn(directive.link);
7217 directive.priority = directive.priority || 0;
7218 directive.index = index;
7219 directive.name = directive.name || name;
7220 directive.require = directive.require || (directive.controller && directive.name);
7221 directive.restrict = directive.restrict || 'EA';
7222 var bindings = directive.$$bindings =
7223 parseDirectiveBindings(directive, directive.name);
7224 if (isObject(bindings.isolateScope)) {
7225 directive.$$isolateBindings = bindings.isolateScope;
7227 directive.$$moduleName = directiveFactory.$$moduleName;
7228 directives.push(directive);
7230 $exceptionHandler(e);
7236 hasDirectives[name].push(directiveFactory);
7238 forEach(name, reverseParams(registerDirective));
7246 * @name $compileProvider#aHrefSanitizationWhitelist
7250 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7251 * urls during a[href] sanitization.
7253 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7255 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7256 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7257 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7258 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7260 * @param {RegExp=} regexp New regexp to whitelist urls with.
7261 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7262 * chaining otherwise.
7264 this.aHrefSanitizationWhitelist = function(regexp) {
7265 if (isDefined(regexp)) {
7266 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7269 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7276 * @name $compileProvider#imgSrcSanitizationWhitelist
7280 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7281 * urls during img[src] sanitization.
7283 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7285 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7286 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7287 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7288 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7290 * @param {RegExp=} regexp New regexp to whitelist urls with.
7291 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7292 * chaining otherwise.
7294 this.imgSrcSanitizationWhitelist = function(regexp) {
7295 if (isDefined(regexp)) {
7296 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7299 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7305 * @name $compileProvider#debugInfoEnabled
7307 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7308 * current debugInfoEnabled state
7309 * @returns {*} current value if used as getter or itself (chaining) if used as setter
7314 * Call this method to enable/disable various debug runtime information in the compiler such as adding
7315 * binding information and a reference to the current scope on to DOM elements.
7316 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7317 * * `ng-binding` CSS class
7318 * * `$binding` data property containing an array of the binding expressions
7320 * You may want to disable this in production for a significant performance boost. See
7321 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7323 * The default value is true.
7325 var debugInfoEnabled = true;
7326 this.debugInfoEnabled = function(enabled) {
7327 if (isDefined(enabled)) {
7328 debugInfoEnabled = enabled;
7331 return debugInfoEnabled;
7335 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7336 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7337 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
7338 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
7340 var Attributes = function(element, attributesToCopy) {
7341 if (attributesToCopy) {
7342 var keys = Object.keys(attributesToCopy);
7345 for (i = 0, l = keys.length; i < l; i++) {
7347 this[key] = attributesToCopy[key];
7353 this.$$element = element;
7356 Attributes.prototype = {
7359 * @name $compile.directive.Attributes#$normalize
7363 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7364 * `data-`) to its normalized, camelCase form.
7366 * Also there is special case for Moz prefix starting with upper case letter.
7368 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7370 * @param {string} name Name to normalize
7372 $normalize: directiveNormalize,
7377 * @name $compile.directive.Attributes#$addClass
7381 * Adds the CSS class value specified by the classVal parameter to the element. If animations
7382 * are enabled then an animation will be triggered for the class addition.
7384 * @param {string} classVal The className value that will be added to the element
7386 $addClass: function(classVal) {
7387 if (classVal && classVal.length > 0) {
7388 $animate.addClass(this.$$element, classVal);
7394 * @name $compile.directive.Attributes#$removeClass
7398 * Removes the CSS class value specified by the classVal parameter from the element. If
7399 * animations are enabled then an animation will be triggered for the class removal.
7401 * @param {string} classVal The className value that will be removed from the element
7403 $removeClass: function(classVal) {
7404 if (classVal && classVal.length > 0) {
7405 $animate.removeClass(this.$$element, classVal);
7411 * @name $compile.directive.Attributes#$updateClass
7415 * Adds and removes the appropriate CSS class values to the element based on the difference
7416 * between the new and old CSS class values (specified as newClasses and oldClasses).
7418 * @param {string} newClasses The current CSS className value
7419 * @param {string} oldClasses The former CSS className value
7421 $updateClass: function(newClasses, oldClasses) {
7422 var toAdd = tokenDifference(newClasses, oldClasses);
7423 if (toAdd && toAdd.length) {
7424 $animate.addClass(this.$$element, toAdd);
7427 var toRemove = tokenDifference(oldClasses, newClasses);
7428 if (toRemove && toRemove.length) {
7429 $animate.removeClass(this.$$element, toRemove);
7434 * Set a normalized attribute on the element in a way such that all directives
7435 * can share the attribute. This function properly handles boolean attributes.
7436 * @param {string} key Normalized key. (ie ngAttribute)
7437 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7438 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7440 * @param {string=} attrName Optional none normalized name. Defaults to key.
7442 $set: function(key, value, writeAttr, attrName) {
7443 // TODO: decide whether or not to throw an error if "class"
7444 //is set through this function since it may cause $updateClass to
7447 var node = this.$$element[0],
7448 booleanKey = getBooleanAttrName(node, key),
7449 aliasedKey = getAliasedAttrName(key),
7454 this.$$element.prop(key, value);
7455 attrName = booleanKey;
7456 } else if (aliasedKey) {
7457 this[aliasedKey] = value;
7458 observer = aliasedKey;
7463 // translate normalized key to actual key
7465 this.$attr[key] = attrName;
7467 attrName = this.$attr[key];
7469 this.$attr[key] = attrName = snake_case(key, '-');
7473 nodeName = nodeName_(this.$$element);
7475 if ((nodeName === 'a' && key === 'href') ||
7476 (nodeName === 'img' && key === 'src')) {
7477 // sanitize a[href] and img[src] values
7478 this[key] = value = $$sanitizeUri(value, key === 'src');
7479 } else if (nodeName === 'img' && key === 'srcset') {
7480 // sanitize img[srcset] values
7483 // first check if there are spaces because it's not the same pattern
7484 var trimmedSrcset = trim(value);
7485 // ( 999x ,| 999w ,| ,|, )
7486 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7487 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7489 // split srcset into tuple of uri and descriptor except for the last item
7490 var rawUris = trimmedSrcset.split(pattern);
7493 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7494 for (var i = 0; i < nbrUrisWith2parts; i++) {
7495 var innerIdx = i * 2;
7497 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7498 // add the descriptor
7499 result += (" " + trim(rawUris[innerIdx + 1]));
7502 // split the last item into uri and descriptor
7503 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7505 // sanitize the last uri
7506 result += $$sanitizeUri(trim(lastTuple[0]), true);
7508 // and add the last descriptor if any
7509 if (lastTuple.length === 2) {
7510 result += (" " + trim(lastTuple[1]));
7512 this[key] = value = result;
7515 if (writeAttr !== false) {
7516 if (value === null || isUndefined(value)) {
7517 this.$$element.removeAttr(attrName);
7519 this.$$element.attr(attrName, value);
7524 var $$observers = this.$$observers;
7525 $$observers && forEach($$observers[observer], function(fn) {
7529 $exceptionHandler(e);
7537 * @name $compile.directive.Attributes#$observe
7541 * Observes an interpolated attribute.
7543 * The observer function will be invoked once during the next `$digest` following
7544 * compilation. The observer is then invoked whenever the interpolated value
7547 * @param {string} key Normalized key. (ie ngAttribute) .
7548 * @param {function(interpolatedValue)} fn Function that will be called whenever
7549 the interpolated value of the attribute changes.
7550 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7551 * @returns {function()} Returns a deregistration function for this observer.
7553 $observe: function(key, fn) {
7555 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7556 listeners = ($$observers[key] || ($$observers[key] = []));
7559 $rootScope.$evalAsync(function() {
7560 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7561 // no one registered attribute interpolation function, so lets call it manually
7567 arrayRemove(listeners, fn);
7573 function safeAddClass($element, className) {
7575 $element.addClass(className);
7577 // ignore, since it means that we are trying to set class on
7578 // SVG element, where class name is read-only.
7583 var startSymbol = $interpolate.startSymbol(),
7584 endSymbol = $interpolate.endSymbol(),
7585 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
7587 : function denormalizeTemplate(template) {
7588 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7590 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7591 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7593 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7594 var bindings = $element.data('$binding') || [];
7596 if (isArray(binding)) {
7597 bindings = bindings.concat(binding);
7599 bindings.push(binding);
7602 $element.data('$binding', bindings);
7605 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7606 safeAddClass($element, 'ng-binding');
7609 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7610 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7611 $element.data(dataName, scope);
7614 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7615 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7620 //================================
7622 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7623 previousCompileContext) {
7624 if (!($compileNodes instanceof jqLite)) {
7625 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7627 $compileNodes = jqLite($compileNodes);
7629 // We can not compile top level text elements since text nodes can be merged and we will
7630 // not be able to attach scope data to them, so we will wrap them in <span>
7631 forEach($compileNodes, function(node, index) {
7632 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7633 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7636 var compositeLinkFn =
7637 compileNodes($compileNodes, transcludeFn, $compileNodes,
7638 maxPriority, ignoreDirective, previousCompileContext);
7639 compile.$$addScopeClass($compileNodes);
7640 var namespace = null;
7641 return function publicLinkFn(scope, cloneConnectFn, options) {
7642 assertArg(scope, 'scope');
7644 if (previousCompileContext && previousCompileContext.needsNewScope) {
7645 // A parent directive did a replace and a directive on this element asked
7646 // for transclusion, which caused us to lose a layer of element on which
7647 // we could hold the new transclusion scope, so we will create it manually
7649 scope = scope.$parent.$new();
7652 options = options || {};
7653 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7654 transcludeControllers = options.transcludeControllers,
7655 futureParentElement = options.futureParentElement;
7657 // When `parentBoundTranscludeFn` is passed, it is a
7658 // `controllersBoundTransclude` function (it was previously passed
7659 // as `transclude` to directive.link) so we must unwrap it to get
7660 // its `boundTranscludeFn`
7661 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7662 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7666 namespace = detectNamespaceForChildElements(futureParentElement);
7669 if (namespace !== 'html') {
7670 // When using a directive with replace:true and templateUrl the $compileNodes
7671 // (or a child element inside of them)
7672 // might change, so we need to recreate the namespace adapted compileNodes
7673 // for call to the link function.
7674 // Note: This will already clone the nodes...
7676 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7678 } else if (cloneConnectFn) {
7679 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7680 // and sometimes changes the structure of the DOM.
7681 $linkNode = JQLitePrototype.clone.call($compileNodes);
7683 $linkNode = $compileNodes;
7686 if (transcludeControllers) {
7687 for (var controllerName in transcludeControllers) {
7688 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7692 compile.$$addScopeInfo($linkNode, scope);
7694 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7695 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7700 function detectNamespaceForChildElements(parentElement) {
7701 // TODO: Make this detect MathML as well...
7702 var node = parentElement && parentElement[0];
7706 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7711 * Compile function matches each node in nodeList against the directives. Once all directives
7712 * for a particular node are collected their compile functions are executed. The compile
7713 * functions return values - the linking functions - are combined into a composite linking
7714 * function, which is the a linking function for the node.
7716 * @param {NodeList} nodeList an array of nodes or NodeList to compile
7717 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7718 * scope argument is auto-generated to the new child of the transcluded parent scope.
7719 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7720 * the rootElement must be set the jqLite collection of the compile root. This is
7721 * needed so that the jqLite collection items can be replaced with widgets.
7722 * @param {number=} maxPriority Max directive priority.
7723 * @returns {Function} A composite linking function of all of the matched directives or null.
7725 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7726 previousCompileContext) {
7728 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7730 for (var i = 0; i < nodeList.length; i++) {
7731 attrs = new Attributes();
7733 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7734 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7737 nodeLinkFn = (directives.length)
7738 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7739 null, [], [], previousCompileContext)
7742 if (nodeLinkFn && nodeLinkFn.scope) {
7743 compile.$$addScopeClass(attrs.$$element);
7746 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7747 !(childNodes = nodeList[i].childNodes) ||
7750 : compileNodes(childNodes,
7752 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7753 && nodeLinkFn.transclude) : transcludeFn);
7755 if (nodeLinkFn || childLinkFn) {
7756 linkFns.push(i, nodeLinkFn, childLinkFn);
7758 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7761 //use the previous context only for the first element in the virtual group
7762 previousCompileContext = null;
7765 // return a linking function if we have found anything, null otherwise
7766 return linkFnFound ? compositeLinkFn : null;
7768 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7769 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7773 if (nodeLinkFnFound) {
7774 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7775 // offsets don't get screwed up
7776 var nodeListLength = nodeList.length;
7777 stableNodeList = new Array(nodeListLength);
7779 // create a sparse array by only copying the elements which have a linkFn
7780 for (i = 0; i < linkFns.length; i+=3) {
7782 stableNodeList[idx] = nodeList[idx];
7785 stableNodeList = nodeList;
7788 for (i = 0, ii = linkFns.length; i < ii;) {
7789 node = stableNodeList[linkFns[i++]];
7790 nodeLinkFn = linkFns[i++];
7791 childLinkFn = linkFns[i++];
7794 if (nodeLinkFn.scope) {
7795 childScope = scope.$new();
7796 compile.$$addScopeInfo(jqLite(node), childScope);
7801 if (nodeLinkFn.transcludeOnThisElement) {
7802 childBoundTranscludeFn = createBoundTranscludeFn(
7803 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7805 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7806 childBoundTranscludeFn = parentBoundTranscludeFn;
7808 } else if (!parentBoundTranscludeFn && transcludeFn) {
7809 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7812 childBoundTranscludeFn = null;
7815 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7817 } else if (childLinkFn) {
7818 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7824 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7826 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7828 if (!transcludedScope) {
7829 transcludedScope = scope.$new(false, containingScope);
7830 transcludedScope.$$transcluded = true;
7833 return transcludeFn(transcludedScope, cloneFn, {
7834 parentBoundTranscludeFn: previousBoundTranscludeFn,
7835 transcludeControllers: controllers,
7836 futureParentElement: futureParentElement
7840 return boundTranscludeFn;
7844 * Looks for directives on the given node and adds them to the directive collection which is
7847 * @param node Node to search.
7848 * @param directives An array to which the directives are added to. This array is sorted before
7849 * the function returns.
7850 * @param attrs The shared attrs object which is used to populate the normalized attributes.
7851 * @param {number=} maxPriority Max directive priority.
7853 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7854 var nodeType = node.nodeType,
7855 attrsMap = attrs.$attr,
7860 case NODE_TYPE_ELEMENT: /* Element */
7861 // use the node name: <directive>
7862 addDirective(directives,
7863 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7865 // iterate over the attributes
7866 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7867 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7868 var attrStartName = false;
7869 var attrEndName = false;
7873 value = trim(attr.value);
7875 // support ngAttr attribute binding
7876 ngAttrName = directiveNormalize(name);
7877 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7878 name = name.replace(PREFIX_REGEXP, '')
7879 .substr(8).replace(/_(.)/g, function(match, letter) {
7880 return letter.toUpperCase();
7884 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7885 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7886 attrStartName = name;
7887 attrEndName = name.substr(0, name.length - 5) + 'end';
7888 name = name.substr(0, name.length - 6);
7891 nName = directiveNormalize(name.toLowerCase());
7892 attrsMap[nName] = name;
7893 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7894 attrs[nName] = value;
7895 if (getBooleanAttrName(node, nName)) {
7896 attrs[nName] = true; // presence means true
7899 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7900 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7904 // use class as directive
7905 className = node.className;
7906 if (isObject(className)) {
7907 // Maybe SVGAnimatedString
7908 className = className.animVal;
7910 if (isString(className) && className !== '') {
7911 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7912 nName = directiveNormalize(match[2]);
7913 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7914 attrs[nName] = trim(match[3]);
7916 className = className.substr(match.index + match[0].length);
7920 case NODE_TYPE_TEXT: /* Text Node */
7922 // Workaround for #11781
7923 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7924 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7925 node.parentNode.removeChild(node.nextSibling);
7928 addTextInterpolateDirective(directives, node.nodeValue);
7930 case NODE_TYPE_COMMENT: /* Comment */
7932 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7934 nName = directiveNormalize(match[1]);
7935 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7936 attrs[nName] = trim(match[2]);
7940 // turns out that under some circumstances IE9 throws errors when one attempts to read
7941 // comment's node value.
7942 // Just ignore it and continue. (Can't seem to reproduce in test case.)
7947 directives.sort(byPriority);
7952 * Given a node with an directive-start it collects all of the siblings until it finds
7959 function groupScan(node, attrStart, attrEnd) {
7962 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7965 throw $compileMinErr('uterdir',
7966 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7967 attrStart, attrEnd);
7969 if (node.nodeType == NODE_TYPE_ELEMENT) {
7970 if (node.hasAttribute(attrStart)) depth++;
7971 if (node.hasAttribute(attrEnd)) depth--;
7974 node = node.nextSibling;
7975 } while (depth > 0);
7980 return jqLite(nodes);
7984 * Wrapper for linking function which converts normal linking function into a grouped
7989 * @returns {Function}
7991 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7992 return function(scope, element, attrs, controllers, transcludeFn) {
7993 element = groupScan(element[0], attrStart, attrEnd);
7994 return linkFn(scope, element, attrs, controllers, transcludeFn);
7999 * Once the directives have been collected, their compile functions are executed. This method
8000 * is responsible for inlining directive templates as well as terminating the application
8001 * of the directives if the terminal directive has been reached.
8003 * @param {Array} directives Array of collected directives to execute their compile function.
8004 * this needs to be pre-sorted by priority order.
8005 * @param {Node} compileNode The raw DOM node to apply the compile functions to
8006 * @param {Object} templateAttrs The shared attribute function
8007 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8008 * scope argument is auto-generated to the new
8009 * child of the transcluded parent scope.
8010 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8011 * argument has the root jqLite array so that we can replace nodes
8013 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8014 * compiling the transclusion.
8015 * @param {Array.<Function>} preLinkFns
8016 * @param {Array.<Function>} postLinkFns
8017 * @param {Object} previousCompileContext Context used for previous compilation of the current
8019 * @returns {Function} linkFn
8021 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8022 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8023 previousCompileContext) {
8024 previousCompileContext = previousCompileContext || {};
8026 var terminalPriority = -Number.MAX_VALUE,
8027 newScopeDirective = previousCompileContext.newScopeDirective,
8028 controllerDirectives = previousCompileContext.controllerDirectives,
8029 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8030 templateDirective = previousCompileContext.templateDirective,
8031 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8032 hasTranscludeDirective = false,
8033 hasTemplate = false,
8034 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8035 $compileNode = templateAttrs.$$element = jqLite(compileNode),
8039 replaceDirective = originalReplaceDirective,
8040 childTranscludeFn = transcludeFn,
8044 // executes all directives on the current element
8045 for (var i = 0, ii = directives.length; i < ii; i++) {
8046 directive = directives[i];
8047 var attrStart = directive.$$start;
8048 var attrEnd = directive.$$end;
8050 // collect multiblock sections
8052 $compileNode = groupScan(compileNode, attrStart, attrEnd);
8054 $template = undefined;
8056 if (terminalPriority > directive.priority) {
8057 break; // prevent further processing of directives
8060 if (directiveValue = directive.scope) {
8062 // skip the check for directives with async templates, we'll check the derived sync
8063 // directive when the template arrives
8064 if (!directive.templateUrl) {
8065 if (isObject(directiveValue)) {
8066 // This directive is trying to add an isolated scope.
8067 // Check that there is no scope of any kind already
8068 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8069 directive, $compileNode);
8070 newIsolateScopeDirective = directive;
8072 // This directive is trying to add a child scope.
8073 // Check that there is no isolated scope already
8074 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8079 newScopeDirective = newScopeDirective || directive;
8082 directiveName = directive.name;
8084 if (!directive.templateUrl && directive.controller) {
8085 directiveValue = directive.controller;
8086 controllerDirectives = controllerDirectives || createMap();
8087 assertNoDuplicate("'" + directiveName + "' controller",
8088 controllerDirectives[directiveName], directive, $compileNode);
8089 controllerDirectives[directiveName] = directive;
8092 if (directiveValue = directive.transclude) {
8093 hasTranscludeDirective = true;
8095 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8096 // This option should only be used by directives that know how to safely handle element transclusion,
8097 // where the transcluded nodes are added or replaced after linking.
8098 if (!directive.$$tlb) {
8099 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8100 nonTlbTranscludeDirective = directive;
8103 if (directiveValue == 'element') {
8104 hasElementTranscludeDirective = true;
8105 terminalPriority = directive.priority;
8106 $template = $compileNode;
8107 $compileNode = templateAttrs.$$element =
8108 jqLite(document.createComment(' ' + directiveName + ': ' +
8109 templateAttrs[directiveName] + ' '));
8110 compileNode = $compileNode[0];
8111 replaceWith(jqCollection, sliceArgs($template), compileNode);
8113 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8114 replaceDirective && replaceDirective.name, {
8116 // - controllerDirectives - otherwise we'll create duplicates controllers
8117 // - newIsolateScopeDirective or templateDirective - combining templates with
8118 // element transclusion doesn't make sense.
8120 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8121 // on the same element more than once.
8122 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8125 $template = jqLite(jqLiteClone(compileNode)).contents();
8126 $compileNode.empty(); // clear contents
8127 childTranscludeFn = compile($template, transcludeFn, undefined,
8128 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8132 if (directive.template) {
8134 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8135 templateDirective = directive;
8137 directiveValue = (isFunction(directive.template))
8138 ? directive.template($compileNode, templateAttrs)
8139 : directive.template;
8141 directiveValue = denormalizeTemplate(directiveValue);
8143 if (directive.replace) {
8144 replaceDirective = directive;
8145 if (jqLiteIsTextNode(directiveValue)) {
8148 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8150 compileNode = $template[0];
8152 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8153 throw $compileMinErr('tplrt',
8154 "Template for directive '{0}' must have exactly one root element. {1}",
8158 replaceWith(jqCollection, $compileNode, compileNode);
8160 var newTemplateAttrs = {$attr: {}};
8162 // combine directives from the original node and from the template:
8163 // - take the array of directives for this element
8164 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8165 // - collect directives from the template and sort them by priority
8166 // - combine directives as: processed + template + unprocessed
8167 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8168 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8170 if (newIsolateScopeDirective || newScopeDirective) {
8171 // The original directive caused the current element to be replaced but this element
8172 // also needs to have a new scope, so we need to tell the template directives
8173 // that they would need to get their scope from further up, if they require transclusion
8174 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8176 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8177 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8179 ii = directives.length;
8181 $compileNode.html(directiveValue);
8185 if (directive.templateUrl) {
8187 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8188 templateDirective = directive;
8190 if (directive.replace) {
8191 replaceDirective = directive;
8194 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8195 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8196 controllerDirectives: controllerDirectives,
8197 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8198 newIsolateScopeDirective: newIsolateScopeDirective,
8199 templateDirective: templateDirective,
8200 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8202 ii = directives.length;
8203 } else if (directive.compile) {
8205 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8206 if (isFunction(linkFn)) {
8207 addLinkFns(null, linkFn, attrStart, attrEnd);
8208 } else if (linkFn) {
8209 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8212 $exceptionHandler(e, startingTag($compileNode));
8216 if (directive.terminal) {
8217 nodeLinkFn.terminal = true;
8218 terminalPriority = Math.max(terminalPriority, directive.priority);
8223 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8224 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8225 nodeLinkFn.templateOnThisElement = hasTemplate;
8226 nodeLinkFn.transclude = childTranscludeFn;
8228 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8230 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8233 ////////////////////
8235 function addLinkFns(pre, post, attrStart, attrEnd) {
8237 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8238 pre.require = directive.require;
8239 pre.directiveName = directiveName;
8240 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8241 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8243 preLinkFns.push(pre);
8246 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8247 post.require = directive.require;
8248 post.directiveName = directiveName;
8249 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8250 post = cloneAndAnnotateFn(post, {isolateScope: true});
8252 postLinkFns.push(post);
8257 function getControllers(directiveName, require, $element, elementControllers) {
8260 if (isString(require)) {
8261 var match = require.match(REQUIRE_PREFIX_REGEXP);
8262 var name = require.substring(match[0].length);
8263 var inheritType = match[1] || match[3];
8264 var optional = match[2] === '?';
8266 //If only parents then start at the parent element
8267 if (inheritType === '^^') {
8268 $element = $element.parent();
8269 //Otherwise attempt getting the controller from elementControllers in case
8270 //the element is transcluded (and has no data) and to avoid .data if possible
8272 value = elementControllers && elementControllers[name];
8273 value = value && value.instance;
8277 var dataName = '$' + name + 'Controller';
8278 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8281 if (!value && !optional) {
8282 throw $compileMinErr('ctreq',
8283 "Controller '{0}', required by directive '{1}', can't be found!",
8284 name, directiveName);
8286 } else if (isArray(require)) {
8288 for (var i = 0, ii = require.length; i < ii; i++) {
8289 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8293 return value || null;
8296 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8297 var elementControllers = createMap();
8298 for (var controllerKey in controllerDirectives) {
8299 var directive = controllerDirectives[controllerKey];
8301 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8304 $transclude: transcludeFn
8307 var controller = directive.controller;
8308 if (controller == '@') {
8309 controller = attrs[directive.name];
8312 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8314 // For directives with element transclusion the element is a comment,
8315 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8316 // clean up (http://bugs.jquery.com/ticket/8335).
8317 // Instead, we save the controllers for the element in a local hash and attach to .data
8318 // later, once we have the actual element.
8319 elementControllers[directive.name] = controllerInstance;
8320 if (!hasElementTranscludeDirective) {
8321 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8324 return elementControllers;
8327 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8328 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8329 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8331 if (compileNode === linkNode) {
8332 attrs = templateAttrs;
8333 $element = templateAttrs.$$element;
8335 $element = jqLite(linkNode);
8336 attrs = new Attributes($element, templateAttrs);
8339 controllerScope = scope;
8340 if (newIsolateScopeDirective) {
8341 isolateScope = scope.$new(true);
8342 } else if (newScopeDirective) {
8343 controllerScope = scope.$parent;
8346 if (boundTranscludeFn) {
8347 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8348 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8349 transcludeFn = controllersBoundTransclude;
8350 transcludeFn.$$boundTransclude = boundTranscludeFn;
8353 if (controllerDirectives) {
8354 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8357 if (newIsolateScopeDirective) {
8358 // Initialize isolate scope bindings for new isolate scope directive.
8359 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8360 templateDirective === newIsolateScopeDirective.$$originalDirective)));
8361 compile.$$addScopeClass($element, true);
8362 isolateScope.$$isolateBindings =
8363 newIsolateScopeDirective.$$isolateBindings;
8364 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8365 isolateScope.$$isolateBindings,
8366 newIsolateScopeDirective);
8367 if (removeScopeBindingWatches) {
8368 isolateScope.$on('$destroy', removeScopeBindingWatches);
8372 // Initialize bindToController bindings
8373 for (var name in elementControllers) {
8374 var controllerDirective = controllerDirectives[name];
8375 var controller = elementControllers[name];
8376 var bindings = controllerDirective.$$bindings.bindToController;
8378 if (controller.identifier && bindings) {
8379 removeControllerBindingWatches =
8380 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8383 var controllerResult = controller();
8384 if (controllerResult !== controller.instance) {
8385 // If the controller constructor has a return value, overwrite the instance
8386 // from setupControllers
8387 controller.instance = controllerResult;
8388 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8389 removeControllerBindingWatches && removeControllerBindingWatches();
8390 removeControllerBindingWatches =
8391 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8396 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8397 linkFn = preLinkFns[i];
8398 invokeLinkFn(linkFn,
8399 linkFn.isolateScope ? isolateScope : scope,
8402 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8408 // We only pass the isolate scope, if the isolate directive has a template,
8409 // otherwise the child elements do not belong to the isolate directive.
8410 var scopeToChild = scope;
8411 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8412 scopeToChild = isolateScope;
8414 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8417 for (i = postLinkFns.length - 1; i >= 0; i--) {
8418 linkFn = postLinkFns[i];
8419 invokeLinkFn(linkFn,
8420 linkFn.isolateScope ? isolateScope : scope,
8423 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8428 // This is the function that is injected as `$transclude`.
8429 // Note: all arguments are optional!
8430 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8431 var transcludeControllers;
8433 // No scope passed in:
8434 if (!isScope(scope)) {
8435 futureParentElement = cloneAttachFn;
8436 cloneAttachFn = scope;
8440 if (hasElementTranscludeDirective) {
8441 transcludeControllers = elementControllers;
8443 if (!futureParentElement) {
8444 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8446 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8451 // Depending upon the context in which a directive finds itself it might need to have a new isolated
8452 // or child scope created. For instance:
8453 // * if the directive has been pulled into a template because another directive with a higher priority
8454 // asked for element transclusion
8455 // * if the directive itself asks for transclusion but it is at the root of a template and the original
8456 // element was replaced. See https://github.com/angular/angular.js/issues/12936
8457 function markDirectiveScope(directives, isolateScope, newScope) {
8458 for (var j = 0, jj = directives.length; j < jj; j++) {
8459 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8464 * looks up the directive and decorates it with exception handling and proper parameters. We
8465 * call this the boundDirective.
8467 * @param {string} name name of the directive to look up.
8468 * @param {string} location The directive must be found in specific format.
8469 * String containing any of theses characters:
8471 * * `E`: element name
8475 * @returns {boolean} true if directive was added.
8477 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8479 if (name === ignoreDirective) return null;
8481 if (hasDirectives.hasOwnProperty(name)) {
8482 for (var directive, directives = $injector.get(name + Suffix),
8483 i = 0, ii = directives.length; i < ii; i++) {
8485 directive = directives[i];
8486 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8487 directive.restrict.indexOf(location) != -1) {
8488 if (startAttrName) {
8489 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8491 tDirectives.push(directive);
8494 } catch (e) { $exceptionHandler(e); }
8502 * looks up the directive and returns true if it is a multi-element directive,
8503 * and therefore requires DOM nodes between -start and -end markers to be grouped
8506 * @param {string} name name of the directive to look up.
8507 * @returns true if directive was registered as multi-element.
8509 function directiveIsMultiElement(name) {
8510 if (hasDirectives.hasOwnProperty(name)) {
8511 for (var directive, directives = $injector.get(name + Suffix),
8512 i = 0, ii = directives.length; i < ii; i++) {
8513 directive = directives[i];
8514 if (directive.multiElement) {
8523 * When the element is replaced with HTML template then the new attributes
8524 * on the template need to be merged with the existing attributes in the DOM.
8525 * The desired effect is to have both of the attributes present.
8527 * @param {object} dst destination attributes (original DOM)
8528 * @param {object} src source attributes (from the directive template)
8530 function mergeTemplateAttributes(dst, src) {
8531 var srcAttr = src.$attr,
8532 dstAttr = dst.$attr,
8533 $element = dst.$$element;
8535 // reapply the old attributes to the new element
8536 forEach(dst, function(value, key) {
8537 if (key.charAt(0) != '$') {
8538 if (src[key] && src[key] !== value) {
8539 value += (key === 'style' ? ';' : ' ') + src[key];
8541 dst.$set(key, value, true, srcAttr[key]);
8545 // copy the new attributes on the old attrs object
8546 forEach(src, function(value, key) {
8547 if (key == 'class') {
8548 safeAddClass($element, value);
8549 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8550 } else if (key == 'style') {
8551 $element.attr('style', $element.attr('style') + ';' + value);
8552 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8553 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8554 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8555 // have an attribute like "has-own-property" or "data-has-own-property", etc.
8556 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8558 dstAttr[key] = srcAttr[key];
8564 function compileTemplateUrl(directives, $compileNode, tAttrs,
8565 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8567 afterTemplateNodeLinkFn,
8568 afterTemplateChildLinkFn,
8569 beforeTemplateCompileNode = $compileNode[0],
8570 origAsyncDirective = directives.shift(),
8571 derivedSyncDirective = inherit(origAsyncDirective, {
8572 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8574 templateUrl = (isFunction(origAsyncDirective.templateUrl))
8575 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8576 : origAsyncDirective.templateUrl,
8577 templateNamespace = origAsyncDirective.templateNamespace;
8579 $compileNode.empty();
8581 $templateRequest(templateUrl)
8582 .then(function(content) {
8583 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8585 content = denormalizeTemplate(content);
8587 if (origAsyncDirective.replace) {
8588 if (jqLiteIsTextNode(content)) {
8591 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8593 compileNode = $template[0];
8595 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8596 throw $compileMinErr('tplrt',
8597 "Template for directive '{0}' must have exactly one root element. {1}",
8598 origAsyncDirective.name, templateUrl);
8601 tempTemplateAttrs = {$attr: {}};
8602 replaceWith($rootElement, $compileNode, compileNode);
8603 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8605 if (isObject(origAsyncDirective.scope)) {
8606 // the original directive that caused the template to be loaded async required
8608 markDirectiveScope(templateDirectives, true);
8610 directives = templateDirectives.concat(directives);
8611 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8613 compileNode = beforeTemplateCompileNode;
8614 $compileNode.html(content);
8617 directives.unshift(derivedSyncDirective);
8619 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8620 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8621 previousCompileContext);
8622 forEach($rootElement, function(node, i) {
8623 if (node == compileNode) {
8624 $rootElement[i] = $compileNode[0];
8627 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8629 while (linkQueue.length) {
8630 var scope = linkQueue.shift(),
8631 beforeTemplateLinkNode = linkQueue.shift(),
8632 linkRootElement = linkQueue.shift(),
8633 boundTranscludeFn = linkQueue.shift(),
8634 linkNode = $compileNode[0];
8636 if (scope.$$destroyed) continue;
8638 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8639 var oldClasses = beforeTemplateLinkNode.className;
8641 if (!(previousCompileContext.hasElementTranscludeDirective &&
8642 origAsyncDirective.replace)) {
8643 // it was cloned therefore we have to clone as well.
8644 linkNode = jqLiteClone(compileNode);
8646 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8648 // Copy in CSS classes from original node
8649 safeAddClass(jqLite(linkNode), oldClasses);
8651 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8652 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8654 childBoundTranscludeFn = boundTranscludeFn;
8656 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8657 childBoundTranscludeFn);
8662 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8663 var childBoundTranscludeFn = boundTranscludeFn;
8664 if (scope.$$destroyed) return;
8666 linkQueue.push(scope,
8669 childBoundTranscludeFn);
8671 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8672 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8674 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8681 * Sorting function for bound directives.
8683 function byPriority(a, b) {
8684 var diff = b.priority - a.priority;
8685 if (diff !== 0) return diff;
8686 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8687 return a.index - b.index;
8690 function assertNoDuplicate(what, previousDirective, directive, element) {
8692 function wrapModuleNameIfDefined(moduleName) {
8694 (' (module: ' + moduleName + ')') :
8698 if (previousDirective) {
8699 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8700 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8701 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8706 function addTextInterpolateDirective(directives, text) {
8707 var interpolateFn = $interpolate(text, true);
8708 if (interpolateFn) {
8711 compile: function textInterpolateCompileFn(templateNode) {
8712 var templateNodeParent = templateNode.parent(),
8713 hasCompileParent = !!templateNodeParent.length;
8715 // When transcluding a template that has bindings in the root
8716 // we don't have a parent and thus need to add the class during linking fn.
8717 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8719 return function textInterpolateLinkFn(scope, node) {
8720 var parent = node.parent();
8721 if (!hasCompileParent) compile.$$addBindingClass(parent);
8722 compile.$$addBindingInfo(parent, interpolateFn.expressions);
8723 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8724 node[0].nodeValue = value;
8733 function wrapTemplate(type, template) {
8734 type = lowercase(type || 'html');
8738 var wrapper = document.createElement('div');
8739 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8740 return wrapper.childNodes[0].childNodes;
8747 function getTrustedContext(node, attrNormalizedName) {
8748 if (attrNormalizedName == "srcdoc") {
8751 var tag = nodeName_(node);
8752 // maction[xlink:href] can source SVG. It's not limited to <maction>.
8753 if (attrNormalizedName == "xlinkHref" ||
8754 (tag == "form" && attrNormalizedName == "action") ||
8755 (tag != "img" && (attrNormalizedName == "src" ||
8756 attrNormalizedName == "ngSrc"))) {
8757 return $sce.RESOURCE_URL;
8762 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8763 var trustedContext = getTrustedContext(node, name);
8764 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8766 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8768 // no interpolation found -> ignore
8769 if (!interpolateFn) return;
8772 if (name === "multiple" && nodeName_(node) === "select") {
8773 throw $compileMinErr("selmulti",
8774 "Binding to the 'multiple' attribute is not supported. Element: {0}",
8780 compile: function() {
8782 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8783 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8785 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8786 throw $compileMinErr('nodomevents',
8787 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8788 "ng- versions (such as ng-click instead of onclick) instead.");
8791 // If the attribute has changed since last $interpolate()ed
8792 var newValue = attr[name];
8793 if (newValue !== value) {
8794 // we need to interpolate again since the attribute value has been updated
8795 // (e.g. by another directive's compile function)
8796 // ensure unset/empty values make interpolateFn falsy
8797 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8801 // if attribute was updated so that there is no interpolation going on we don't want to
8802 // register any observers
8803 if (!interpolateFn) return;
8805 // initialize attr object so that it's ready in case we need the value for isolate
8806 // scope initialization, otherwise the value would not be available from isolate
8807 // directive's linking fn during linking phase
8808 attr[name] = interpolateFn(scope);
8810 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8811 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8812 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8813 //special case for class attribute addition + removal
8814 //so that class changes can tap into the animation
8815 //hooks provided by the $animate service. Be sure to
8816 //skip animations when the first digest occurs (when
8817 //both the new and the old values are the same) since
8818 //the CSS classes are the non-interpolated values
8819 if (name === 'class' && newValue != oldValue) {
8820 attr.$updateClass(newValue, oldValue);
8822 attr.$set(name, newValue);
8833 * This is a special jqLite.replaceWith, which can replace items which
8834 * have no parents, provided that the containing jqLite collection is provided.
8836 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8837 * in the root of the tree.
8838 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8839 * the shell, but replace its DOM node reference.
8840 * @param {Node} newNode The new DOM node.
8842 function replaceWith($rootElement, elementsToRemove, newNode) {
8843 var firstElementToRemove = elementsToRemove[0],
8844 removeCount = elementsToRemove.length,
8845 parent = firstElementToRemove.parentNode,
8849 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8850 if ($rootElement[i] == firstElementToRemove) {
8851 $rootElement[i++] = newNode;
8852 for (var j = i, j2 = j + removeCount - 1,
8853 jj = $rootElement.length;
8854 j < jj; j++, j2++) {
8856 $rootElement[j] = $rootElement[j2];
8858 delete $rootElement[j];
8861 $rootElement.length -= removeCount - 1;
8863 // If the replaced element is also the jQuery .context then replace it
8864 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8865 // http://api.jquery.com/context/
8866 if ($rootElement.context === firstElementToRemove) {
8867 $rootElement.context = newNode;
8875 parent.replaceChild(newNode, firstElementToRemove);
8878 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8879 var fragment = document.createDocumentFragment();
8880 fragment.appendChild(firstElementToRemove);
8882 if (jqLite.hasData(firstElementToRemove)) {
8883 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8884 // data here because there's no public interface in jQuery to do that and copying over
8885 // event listeners (which is the main use of private data) wouldn't work anyway.
8886 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8888 // Remove data of the replaced element. We cannot just call .remove()
8889 // on the element it since that would deallocate scope that is needed
8890 // for the new node. Instead, remove the data "manually".
8892 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8894 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8895 // the replaced element. The cleanData version monkey-patched by Angular would cause
8896 // the scope to be trashed and we do need the very same scope to work with the new
8897 // element. However, we cannot just cache the non-patched version and use it here as
8898 // that would break if another library patches the method after Angular does (one
8899 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8900 // skipped this one time.
8901 skipDestroyOnNextJQueryCleanData = true;
8902 jQuery.cleanData([firstElementToRemove]);
8906 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8907 var element = elementsToRemove[k];
8908 jqLite(element).remove(); // must do this way to clean up expando
8909 fragment.appendChild(element);
8910 delete elementsToRemove[k];
8913 elementsToRemove[0] = newNode;
8914 elementsToRemove.length = 1;
8918 function cloneAndAnnotateFn(fn, annotation) {
8919 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8923 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8925 linkFn(scope, $element, attrs, controllers, transcludeFn);
8927 $exceptionHandler(e, startingTag($element));
8932 // Set up $watches for isolate scope and controller bindings. This process
8933 // only occurs for isolate scopes and new scopes with controllerAs.
8934 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8935 var removeWatchCollection = [];
8936 forEach(bindings, function(definition, scopeName) {
8937 var attrName = definition.attrName,
8938 optional = definition.optional,
8939 mode = definition.mode, // @, =, or &
8941 parentGet, parentSet, compare;
8946 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8947 destination[scopeName] = attrs[attrName] = void 0;
8949 attrs.$observe(attrName, function(value) {
8950 if (isString(value)) {
8951 destination[scopeName] = value;
8954 attrs.$$observers[attrName].$$scope = scope;
8955 if (isString(attrs[attrName])) {
8956 // If the attribute has been provided then we trigger an interpolation to ensure
8957 // the value is there for use in the link fn
8958 destination[scopeName] = $interpolate(attrs[attrName])(scope);
8963 if (!hasOwnProperty.call(attrs, attrName)) {
8964 if (optional) break;
8965 attrs[attrName] = void 0;
8967 if (optional && !attrs[attrName]) break;
8969 parentGet = $parse(attrs[attrName]);
8970 if (parentGet.literal) {
8973 compare = function(a, b) { return a === b || (a !== a && b !== b); };
8975 parentSet = parentGet.assign || function() {
8976 // reset the change, or we will throw this exception on every $digest
8977 lastValue = destination[scopeName] = parentGet(scope);
8978 throw $compileMinErr('nonassign',
8979 "Expression '{0}' used with directive '{1}' is non-assignable!",
8980 attrs[attrName], directive.name);
8982 lastValue = destination[scopeName] = parentGet(scope);
8983 var parentValueWatch = function parentValueWatch(parentValue) {
8984 if (!compare(parentValue, destination[scopeName])) {
8985 // we are out of sync and need to copy
8986 if (!compare(parentValue, lastValue)) {
8987 // parent changed and it has precedence
8988 destination[scopeName] = parentValue;
8990 // if the parent can be assigned then do so
8991 parentSet(scope, parentValue = destination[scopeName]);
8994 return lastValue = parentValue;
8996 parentValueWatch.$stateful = true;
8998 if (definition.collection) {
8999 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9001 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9003 removeWatchCollection.push(removeWatch);
9007 // Don't assign Object.prototype method to scope
9008 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9010 // Don't assign noop to destination if expression is not valid
9011 if (parentGet === noop && optional) break;
9013 destination[scopeName] = function(locals) {
9014 return parentGet(scope, locals);
9020 return removeWatchCollection.length && function removeWatches() {
9021 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9022 removeWatchCollection[i]();
9029 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9031 * Converts all accepted directives format into proper directive name.
9032 * @param name Name to normalize
9034 function directiveNormalize(name) {
9035 return camelCase(name.replace(PREFIX_REGEXP, ''));
9040 * @name $compile.directive.Attributes
9043 * A shared object between directive compile / linking functions which contains normalized DOM
9044 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9045 * needed since all of these are treated as equivalent in Angular:
9048 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9054 * @name $compile.directive.Attributes#$attr
9057 * A map of DOM element attribute names to the normalized name. This is
9058 * needed to do reverse lookup from normalized name back to actual name.
9064 * @name $compile.directive.Attributes#$set
9068 * Set DOM element attribute value.
9071 * @param {string} name Normalized element attribute name of the property to modify. The name is
9072 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9073 * property to the original name.
9074 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9080 * Closure compiler type information
9083 function nodesetLinkingFn(
9084 /* angular.Scope */ scope,
9085 /* NodeList */ nodeList,
9086 /* Element */ rootElement,
9087 /* function(Function) */ boundTranscludeFn
9090 function directiveLinkingFn(
9091 /* nodesetLinkingFn */ nodesetLinkingFn,
9092 /* angular.Scope */ scope,
9094 /* Element */ rootElement,
9095 /* function(Function) */ boundTranscludeFn
9098 function tokenDifference(str1, str2) {
9100 tokens1 = str1.split(/\s+/),
9101 tokens2 = str2.split(/\s+/);
9104 for (var i = 0; i < tokens1.length; i++) {
9105 var token = tokens1[i];
9106 for (var j = 0; j < tokens2.length; j++) {
9107 if (token == tokens2[j]) continue outer;
9109 values += (values.length > 0 ? ' ' : '') + token;
9114 function removeComments(jqNodes) {
9115 jqNodes = jqLite(jqNodes);
9116 var i = jqNodes.length;
9123 var node = jqNodes[i];
9124 if (node.nodeType === NODE_TYPE_COMMENT) {
9125 splice.call(jqNodes, i, 1);
9131 var $controllerMinErr = minErr('$controller');
9134 var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9135 function identifierForController(controller, ident) {
9136 if (ident && isString(ident)) return ident;
9137 if (isString(controller)) {
9138 var match = CNTRL_REG.exec(controller);
9139 if (match) return match[3];
9146 * @name $controllerProvider
9148 * The {@link ng.$controller $controller service} is used by Angular to create new
9151 * This provider allows controller registration via the
9152 * {@link ng.$controllerProvider#register register} method.
9154 function $ControllerProvider() {
9155 var controllers = {},
9160 * @name $controllerProvider#register
9161 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9162 * the names and the values are the constructors.
9163 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9164 * annotations in the array notation).
9166 this.register = function(name, constructor) {
9167 assertNotHasOwnProperty(name, 'controller');
9168 if (isObject(name)) {
9169 extend(controllers, name);
9171 controllers[name] = constructor;
9177 * @name $controllerProvider#allowGlobals
9178 * @description If called, allows `$controller` to find controller constructors on `window`
9180 this.allowGlobals = function() {
9185 this.$get = ['$injector', '$window', function($injector, $window) {
9190 * @requires $injector
9192 * @param {Function|string} constructor If called with a function then it's considered to be the
9193 * controller constructor function. Otherwise it's considered to be a string which is used
9194 * to retrieve the controller constructor using the following steps:
9196 * * check if a controller with given name is registered via `$controllerProvider`
9197 * * check if evaluating the string on the current scope returns a constructor
9198 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9199 * `window` object (not recommended)
9201 * The string can use the `controller as property` syntax, where the controller instance is published
9202 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9203 * to work correctly.
9205 * @param {Object} locals Injection locals for Controller.
9206 * @return {Object} Instance of given controller.
9209 * `$controller` service is responsible for instantiating controllers.
9211 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9212 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9214 return function(expression, locals, later, ident) {
9216 // param `later` --- indicates that the controller's constructor is invoked at a later time.
9217 // If true, $controller will allocate the object with the correct
9218 // prototype chain, but will not invoke the controller until a returned
9219 // callback is invoked.
9220 // param `ident` --- An optional label which overrides the label parsed from the controller
9221 // expression, if any.
9222 var instance, match, constructor, identifier;
9223 later = later === true;
9224 if (ident && isString(ident)) {
9228 if (isString(expression)) {
9229 match = expression.match(CNTRL_REG);
9231 throw $controllerMinErr('ctrlfmt',
9232 "Badly formed controller string '{0}'. " +
9233 "Must match `__name__ as __id__` or `__name__`.", expression);
9235 constructor = match[1],
9236 identifier = identifier || match[3];
9237 expression = controllers.hasOwnProperty(constructor)
9238 ? controllers[constructor]
9239 : getter(locals.$scope, constructor, true) ||
9240 (globals ? getter($window, constructor, true) : undefined);
9242 assertArgFn(expression, constructor, true);
9246 // Instantiate controller later:
9247 // This machinery is used to create an instance of the object before calling the
9248 // controller's constructor itself.
9250 // This allows properties to be added to the controller before the constructor is
9251 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9253 // This feature is not intended for use by applications, and is thus not documented
9255 // Object creation: http://jsperf.com/create-constructor/2
9256 var controllerPrototype = (isArray(expression) ?
9257 expression[expression.length - 1] : expression).prototype;
9258 instance = Object.create(controllerPrototype || null);
9261 addIdentifier(locals, identifier, instance, constructor || expression.name);
9265 return instantiate = extend(function() {
9266 var result = $injector.invoke(expression, instance, locals, constructor);
9267 if (result !== instance && (isObject(result) || isFunction(result))) {
9270 // If result changed, re-assign controllerAs value to scope.
9271 addIdentifier(locals, identifier, instance, constructor || expression.name);
9277 identifier: identifier
9281 instance = $injector.instantiate(expression, locals, constructor);
9284 addIdentifier(locals, identifier, instance, constructor || expression.name);
9290 function addIdentifier(locals, identifier, instance, name) {
9291 if (!(locals && isObject(locals.$scope))) {
9292 throw minErr('$controller')('noscp',
9293 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9297 locals.$scope[identifier] = instance;
9308 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9311 <example module="documentExample">
9312 <file name="index.html">
9313 <div ng-controller="ExampleController">
9314 <p>$document title: <b ng-bind="title"></b></p>
9315 <p>window.document title: <b ng-bind="windowTitle"></b></p>
9318 <file name="script.js">
9319 angular.module('documentExample', [])
9320 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9321 $scope.title = $document[0].title;
9322 $scope.windowTitle = angular.element(window.document)[0].title;
9327 function $DocumentProvider() {
9328 this.$get = ['$window', function(window) {
9329 return jqLite(window.document);
9335 * @name $exceptionHandler
9339 * Any uncaught exception in angular expressions is delegated to this service.
9340 * The default implementation simply delegates to `$log.error` which logs it into
9341 * the browser console.
9343 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9344 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9349 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9350 * return function(exception, cause) {
9351 * exception.message += ' (caused by "' + cause + '")';
9357 * This example will override the normal action of `$exceptionHandler`, to make angular
9358 * exceptions fail hard when they happen, instead of just logging to the console.
9361 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9362 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9363 * (unless executed during a digest).
9365 * If you wish, you can manually delegate exceptions, e.g.
9366 * `try { ... } catch(e) { $exceptionHandler(e); }`
9368 * @param {Error} exception Exception associated with the error.
9369 * @param {string=} cause optional information about the context in which
9370 * the error was thrown.
9373 function $ExceptionHandlerProvider() {
9374 this.$get = ['$log', function($log) {
9375 return function(exception, cause) {
9376 $log.error.apply($log, arguments);
9381 var $$ForceReflowProvider = function() {
9382 this.$get = ['$document', function($document) {
9383 return function(domNode) {
9384 //the line below will force the browser to perform a repaint so
9385 //that all the animated elements within the animation frame will
9386 //be properly updated and drawn on screen. This is required to
9387 //ensure that the preparation animation is properly flushed so that
9388 //the active state picks up from there. DO NOT REMOVE THIS LINE.
9389 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9390 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9391 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9393 if (!domNode.nodeType && domNode instanceof jqLite) {
9394 domNode = domNode[0];
9397 domNode = $document[0].body;
9399 return domNode.offsetWidth + 1;
9404 var APPLICATION_JSON = 'application/json';
9405 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9406 var JSON_START = /^\[|^\{(?!\{)/;
9411 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9412 var $httpMinErr = minErr('$http');
9413 var $httpMinErrLegacyFn = function(method) {
9415 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9419 function serializeValue(v) {
9421 return isDate(v) ? v.toISOString() : toJson(v);
9427 function $HttpParamSerializerProvider() {
9430 * @name $httpParamSerializer
9433 * Default {@link $http `$http`} params serializer that converts objects to strings
9434 * according to the following rules:
9436 * * `{'foo': 'bar'}` results in `foo=bar`
9437 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9438 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9439 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9441 * Note that serializer will sort the request parameters alphabetically.
9444 this.$get = function() {
9445 return function ngParamSerializer(params) {
9446 if (!params) return '';
9448 forEachSorted(params, function(value, key) {
9449 if (value === null || isUndefined(value)) return;
9450 if (isArray(value)) {
9451 forEach(value, function(v, k) {
9452 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
9455 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9459 return parts.join('&');
9464 function $HttpParamSerializerJQLikeProvider() {
9467 * @name $httpParamSerializerJQLike
9470 * Alternative {@link $http `$http`} params serializer that follows
9471 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9472 * The serializer will also sort the params alphabetically.
9474 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9481 * paramSerializer: '$httpParamSerializerJQLike'
9485 * It is also possible to set it as the default `paramSerializer` in the
9486 * {@link $httpProvider#defaults `$httpProvider`}.
9488 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9489 * form data for submission:
9492 * .controller(function($http, $httpParamSerializerJQLike) {
9498 * data: $httpParamSerializerJQLike(myData),
9500 * 'Content-Type': 'application/x-www-form-urlencoded'
9508 this.$get = function() {
9509 return function jQueryLikeParamSerializer(params) {
9510 if (!params) return '';
9512 serialize(params, '', true);
9513 return parts.join('&');
9515 function serialize(toSerialize, prefix, topLevel) {
9516 if (toSerialize === null || isUndefined(toSerialize)) return;
9517 if (isArray(toSerialize)) {
9518 forEach(toSerialize, function(value, index) {
9519 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9521 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9522 forEachSorted(toSerialize, function(value, key) {
9523 serialize(value, prefix +
9524 (topLevel ? '' : '[') +
9526 (topLevel ? '' : ']'));
9529 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9536 function defaultHttpResponseTransform(data, headers) {
9537 if (isString(data)) {
9538 // Strip json vulnerability protection prefix and trim whitespace
9539 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9542 var contentType = headers('Content-Type');
9543 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9544 data = fromJson(tempData);
9552 function isJsonLike(str) {
9553 var jsonStart = str.match(JSON_START);
9554 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9558 * Parse headers into key value object
9560 * @param {string} headers Raw headers as a string
9561 * @returns {Object} Parsed headers as key value object
9563 function parseHeaders(headers) {
9564 var parsed = createMap(), i;
9566 function fillInParsed(key, val) {
9568 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9572 if (isString(headers)) {
9573 forEach(headers.split('\n'), function(line) {
9574 i = line.indexOf(':');
9575 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9577 } else if (isObject(headers)) {
9578 forEach(headers, function(headerVal, headerKey) {
9579 fillInParsed(lowercase(headerKey), trim(headerVal));
9588 * Returns a function that provides access to parsed headers.
9590 * Headers are lazy parsed when first requested.
9593 * @param {(string|Object)} headers Headers to provide access to.
9594 * @returns {function(string=)} Returns a getter function which if called with:
9596 * - if called with single an argument returns a single header value or null
9597 * - if called with no arguments returns an object containing all headers.
9599 function headersGetter(headers) {
9602 return function(name) {
9603 if (!headersObj) headersObj = parseHeaders(headers);
9606 var value = headersObj[lowercase(name)];
9607 if (value === void 0) {
9619 * Chain all given functions
9621 * This function is used for both request and response transforming
9623 * @param {*} data Data to transform.
9624 * @param {function(string=)} headers HTTP headers getter fn.
9625 * @param {number} status HTTP status code of the response.
9626 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9627 * @returns {*} Transformed data.
9629 function transformData(data, headers, status, fns) {
9630 if (isFunction(fns)) {
9631 return fns(data, headers, status);
9634 forEach(fns, function(fn) {
9635 data = fn(data, headers, status);
9642 function isSuccess(status) {
9643 return 200 <= status && status < 300;
9649 * @name $httpProvider
9651 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9653 function $HttpProvider() {
9656 * @name $httpProvider#defaults
9659 * Object containing default values for all {@link ng.$http $http} requests.
9661 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9662 * that will provide the cache for all requests who set their `cache` property to `true`.
9663 * If you set the `defaults.cache = false` then only requests that specify their own custom
9664 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9666 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9667 * Defaults value is `'XSRF-TOKEN'`.
9669 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9670 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9672 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9673 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9674 * setting default headers.
9675 * - **`defaults.headers.common`**
9676 * - **`defaults.headers.post`**
9677 * - **`defaults.headers.put`**
9678 * - **`defaults.headers.patch`**
9681 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9682 * used to the prepare string representation of request parameters (specified as an object).
9683 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9684 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9687 var defaults = this.defaults = {
9688 // transform incoming response data
9689 transformResponse: [defaultHttpResponseTransform],
9691 // transform outgoing request data
9692 transformRequest: [function(d) {
9693 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9699 'Accept': 'application/json, text/plain, */*'
9701 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9702 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9703 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9706 xsrfCookieName: 'XSRF-TOKEN',
9707 xsrfHeaderName: 'X-XSRF-TOKEN',
9709 paramSerializer: '$httpParamSerializer'
9712 var useApplyAsync = false;
9715 * @name $httpProvider#useApplyAsync
9718 * Configure $http service to combine processing of multiple http responses received at around
9719 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9720 * significant performance improvement for bigger applications that make many HTTP requests
9721 * concurrently (common during application bootstrap).
9723 * Defaults to false. If no value is specified, returns the current configured value.
9725 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9726 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9727 * to load and share the same digest cycle.
9729 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9730 * otherwise, returns the current configured value.
9732 this.useApplyAsync = function(value) {
9733 if (isDefined(value)) {
9734 useApplyAsync = !!value;
9737 return useApplyAsync;
9740 var useLegacyPromise = true;
9743 * @name $httpProvider#useLegacyPromiseExtensions
9746 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9747 * This should be used to make sure that applications work without these methods.
9749 * Defaults to true. If no value is specified, returns the current configured value.
9751 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9753 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9754 * otherwise, returns the current configured value.
9756 this.useLegacyPromiseExtensions = function(value) {
9757 if (isDefined(value)) {
9758 useLegacyPromise = !!value;
9761 return useLegacyPromise;
9766 * @name $httpProvider#interceptors
9769 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9770 * pre-processing of request or postprocessing of responses.
9772 * These service factories are ordered by request, i.e. they are applied in the same order as the
9773 * array, on request, but reverse order, on response.
9775 * {@link ng.$http#interceptors Interceptors detailed info}
9777 var interceptorFactories = this.interceptors = [];
9779 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9780 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9782 var defaultCache = $cacheFactory('$http');
9785 * Make sure that default param serializer is exposed as a function
9787 defaults.paramSerializer = isString(defaults.paramSerializer) ?
9788 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9791 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9792 * The reversal is needed so that we can build up the interception chain around the
9795 var reversedInterceptors = [];
9797 forEach(interceptorFactories, function(interceptorFactory) {
9798 reversedInterceptors.unshift(isString(interceptorFactory)
9799 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9806 * @requires ng.$httpBackend
9807 * @requires $cacheFactory
9808 * @requires $rootScope
9810 * @requires $injector
9813 * The `$http` service is a core Angular service that facilitates communication with the remote
9814 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9815 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9817 * For unit testing applications that use `$http` service, see
9818 * {@link ngMock.$httpBackend $httpBackend mock}.
9820 * For a higher level of abstraction, please check out the {@link ngResource.$resource
9821 * $resource} service.
9823 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9824 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9825 * it is important to familiarize yourself with these APIs and the guarantees they provide.
9829 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9830 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
9833 * // Simple GET request example:
9837 * }).then(function successCallback(response) {
9838 * // this callback will be called asynchronously
9839 * // when the response is available
9840 * }, function errorCallback(response) {
9841 * // called asynchronously if an error occurs
9842 * // or server returns response with an error status.
9846 * The response object has these properties:
9848 * - **data** – `{string|Object}` – The response body transformed with the transform
9850 * - **status** – `{number}` – HTTP status code of the response.
9851 * - **headers** – `{function([headerName])}` – Header getter function.
9852 * - **config** – `{Object}` – The configuration object that was used to generate the request.
9853 * - **statusText** – `{string}` – HTTP status text of the response.
9855 * A response status code between 200 and 299 is considered a success status and
9856 * will result in the success callback being called. Note that if the response is a redirect,
9857 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9858 * called for such responses.
9861 * ## Shortcut methods
9863 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9864 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9868 * $http.get('/someUrl', config).then(successCallback, errorCallback);
9869 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9872 * Complete list of shortcut methods:
9874 * - {@link ng.$http#get $http.get}
9875 * - {@link ng.$http#head $http.head}
9876 * - {@link ng.$http#post $http.post}
9877 * - {@link ng.$http#put $http.put}
9878 * - {@link ng.$http#delete $http.delete}
9879 * - {@link ng.$http#jsonp $http.jsonp}
9880 * - {@link ng.$http#patch $http.patch}
9883 * ## Writing Unit Tests that use $http
9884 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9885 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9886 * request using trained responses.
9889 * $httpBackend.expectGET(...);
9891 * $httpBackend.flush();
9894 * ## Deprecation Notice
9895 * <div class="alert alert-danger">
9896 * The `$http` legacy promise methods `success` and `error` have been deprecated.
9897 * Use the standard `then` method instead.
9898 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9899 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9902 * ## Setting HTTP Headers
9904 * The $http service will automatically add certain HTTP headers to all requests. These defaults
9905 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9906 * object, which currently contains this default configuration:
9908 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9909 * - `Accept: application/json, text/plain, * / *`
9910 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9911 * - `Content-Type: application/json`
9912 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9913 * - `Content-Type: application/json`
9915 * To add or overwrite these defaults, simply add or remove a property from these configuration
9916 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9917 * with the lowercased HTTP method name as the key, e.g.
9918 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9920 * The defaults can also be set at runtime via the `$http.defaults` object in the same
9921 * fashion. For example:
9924 * module.run(function($http) {
9925 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9929 * In addition, you can supply a `headers` property in the config object passed when
9930 * calling `$http(config)`, which overrides the defaults without changing them globally.
9932 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9933 * Use the `headers` property, setting the desired header to `undefined`. For example:
9938 * url: 'http://example.com',
9940 * 'Content-Type': undefined
9942 * data: { test: 'test' }
9945 * $http(req).then(function(){...}, function(){...});
9948 * ## Transforming Requests and Responses
9950 * Both requests and responses can be transformed using transformation functions: `transformRequest`
9951 * and `transformResponse`. These properties can be a single function that returns
9952 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9953 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9955 * ### Default Transformations
9957 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9958 * `defaults.transformResponse` properties. If a request does not provide its own transformations
9959 * then these will be applied.
9961 * You can augment or replace the default transformations by modifying these properties by adding to or
9962 * replacing the array.
9964 * Angular provides the following default transformations:
9966 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9968 * - If the `data` property of the request configuration object contains an object, serialize it
9971 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9973 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
9974 * - If JSON response is detected, deserialize it using a JSON parser.
9977 * ### Overriding the Default Transformations Per Request
9979 * If you wish override the request/response transformations only for a single request then provide
9980 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9983 * Note that if you provide these properties on the config object the default transformations will be
9984 * overwritten. If you wish to augment the default transformations then you must include them in your
9985 * local transformation array.
9987 * The following code demonstrates adding a new response transformation to be run after the default response
9988 * transformations have been run.
9991 * function appendTransform(defaults, transform) {
9993 * // We can't guarantee that the default transformation is an array
9994 * defaults = angular.isArray(defaults) ? defaults : [defaults];
9996 * // Append the new transformation to the defaults
9997 * return defaults.concat(transform);
10003 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10004 * return doTransform(value);
10012 * To enable caching, set the request configuration `cache` property to `true` (to use default
10013 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10014 * When the cache is enabled, `$http` stores the response from the server in the specified
10015 * cache. The next time the same request is made, the response is served from the cache without
10016 * sending a request to the server.
10018 * Note that even if the response is served from cache, delivery of the data is asynchronous in
10019 * the same way that real requests are.
10021 * If there are multiple GET requests for the same URL that should be cached using the same
10022 * cache, but the cache is not populated yet, only one request to the server will be made and
10023 * the remaining requests will be fulfilled using the response from the first request.
10025 * You can change the default cache to a new object (built with
10026 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10027 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10028 * their `cache` property to `true` will now use this cache object.
10030 * If you set the default cache to `false` then only requests that specify their own custom
10031 * cache object will be cached.
10035 * Before you start creating interceptors, be sure to understand the
10036 * {@link ng.$q $q and deferred/promise APIs}.
10038 * For purposes of global error handling, authentication, or any kind of synchronous or
10039 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10040 * able to intercept requests before they are handed to the server and
10041 * responses before they are handed over to the application code that
10042 * initiated these requests. The interceptors leverage the {@link ng.$q
10043 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10045 * The interceptors are service factories that are registered with the `$httpProvider` by
10046 * adding them to the `$httpProvider.interceptors` array. The factory is called and
10047 * injected with dependencies (if specified) and returns the interceptor.
10049 * There are two kinds of interceptors (and two kinds of rejection interceptors):
10051 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10052 * modify the `config` object or create a new one. The function needs to return the `config`
10053 * object directly, or a promise containing the `config` or a new `config` object.
10054 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
10055 * resolved with a rejection.
10056 * * `response`: interceptors get called with http `response` object. The function is free to
10057 * modify the `response` object or create a new one. The function needs to return the `response`
10058 * object directly, or as a promise containing the `response` or a new `response` object.
10059 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
10060 * resolved with a rejection.
10064 * // register the interceptor as a service
10065 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10067 * // optional method
10068 * 'request': function(config) {
10069 * // do something on success
10073 * // optional method
10074 * 'requestError': function(rejection) {
10075 * // do something on error
10076 * if (canRecover(rejection)) {
10077 * return responseOrNewPromise
10079 * return $q.reject(rejection);
10084 * // optional method
10085 * 'response': function(response) {
10086 * // do something on success
10090 * // optional method
10091 * 'responseError': function(rejection) {
10092 * // do something on error
10093 * if (canRecover(rejection)) {
10094 * return responseOrNewPromise
10096 * return $q.reject(rejection);
10101 * $httpProvider.interceptors.push('myHttpInterceptor');
10104 * // alternatively, register the interceptor via an anonymous factory
10105 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10107 * 'request': function(config) {
10111 * 'response': function(response) {
10118 * ## Security Considerations
10120 * When designing web applications, consider security threats from:
10122 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10123 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10125 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10126 * pre-configured with strategies that address these issues, but for this to work backend server
10127 * cooperation is required.
10129 * ### JSON Vulnerability Protection
10131 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10132 * allows third party website to turn your JSON resource URL into
10133 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10134 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10135 * Angular will automatically strip the prefix before processing it as JSON.
10137 * For example if your server needs to return:
10142 * which is vulnerable to attack, your server can return:
10148 * Angular will strip the prefix, before processing the JSON.
10151 * ### Cross Site Request Forgery (XSRF) Protection
10153 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10154 * an unauthorized site can gain your user's private data. Angular provides a mechanism
10155 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10156 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10157 * JavaScript that runs on your domain could read the cookie, your server can be assured that
10158 * the XHR came from JavaScript running on your domain. The header will not be set for
10159 * cross-domain requests.
10161 * To take advantage of this, your server needs to set a token in a JavaScript readable session
10162 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10163 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10164 * that only JavaScript running on your domain could have sent the request. The token must be
10165 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10166 * making up its own tokens). We recommend that the token is a digest of your site's
10167 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
10168 * for added security.
10170 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10171 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10172 * or the per-request config object.
10174 * In order to prevent collisions in environments where multiple Angular apps share the
10175 * same domain or subdomain, we recommend that each application uses unique cookie name.
10177 * @param {object} config Object describing the request to be made and how it should be
10178 * processed. The object has following properties:
10180 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10181 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10182 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10183 * with the `paramSerializer` and appended as GET parameters.
10184 * - **data** – `{string|Object}` – Data to be sent as the request message data.
10185 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
10186 * HTTP headers to send to the server. If the return value of a function is null, the
10187 * header will not be sent. Functions accept a config object as an argument.
10188 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10189 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10190 * - **transformRequest** –
10191 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10192 * transform function or an array of such functions. The transform function takes the http
10193 * request body and headers and returns its transformed (typically serialized) version.
10194 * See {@link ng.$http#overriding-the-default-transformations-per-request
10195 * Overriding the Default Transformations}
10196 * - **transformResponse** –
10197 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10198 * transform function or an array of such functions. The transform function takes the http
10199 * response body, headers and status and returns its transformed (typically deserialized) version.
10200 * See {@link ng.$http#overriding-the-default-transformations-per-request
10201 * Overriding the Default TransformationjqLiks}
10202 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10203 * prepare the string representation of request parameters (specified as an object).
10204 * If specified as string, it is interpreted as function registered with the
10205 * {@link $injector $injector}, which means you can create your own serializer
10206 * by registering it as a {@link auto.$provide#service service}.
10207 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10208 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10209 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10210 * GET request, otherwise if a cache instance built with
10211 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10213 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10214 * that should abort the request when resolved.
10215 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10216 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10217 * for more information.
10218 * - **responseType** - `{string}` - see
10219 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10221 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10222 * when the request succeeds or fails.
10225 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10226 * requests. This is primarily meant to be used for debugging purposes.
10230 <example module="httpExample">
10231 <file name="index.html">
10232 <div ng-controller="FetchController">
10233 <select ng-model="method" aria-label="Request method">
10234 <option>GET</option>
10235 <option>JSONP</option>
10237 <input type="text" ng-model="url" size="80" aria-label="URL" />
10238 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10239 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10240 <button id="samplejsonpbtn"
10241 ng-click="updateModel('JSONP',
10242 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10245 <button id="invalidjsonpbtn"
10246 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10249 <pre>http status code: {{status}}</pre>
10250 <pre>http response data: {{data}}</pre>
10253 <file name="script.js">
10254 angular.module('httpExample', [])
10255 .controller('FetchController', ['$scope', '$http', '$templateCache',
10256 function($scope, $http, $templateCache) {
10257 $scope.method = 'GET';
10258 $scope.url = 'http-hello.html';
10260 $scope.fetch = function() {
10261 $scope.code = null;
10262 $scope.response = null;
10264 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10265 then(function(response) {
10266 $scope.status = response.status;
10267 $scope.data = response.data;
10268 }, function(response) {
10269 $scope.data = response.data || "Request failed";
10270 $scope.status = response.status;
10274 $scope.updateModel = function(method, url) {
10275 $scope.method = method;
10280 <file name="http-hello.html">
10283 <file name="protractor.js" type="protractor">
10284 var status = element(by.binding('status'));
10285 var data = element(by.binding('data'));
10286 var fetchBtn = element(by.id('fetchbtn'));
10287 var sampleGetBtn = element(by.id('samplegetbtn'));
10288 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10289 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10291 it('should make an xhr GET request', function() {
10292 sampleGetBtn.click();
10294 expect(status.getText()).toMatch('200');
10295 expect(data.getText()).toMatch(/Hello, \$http!/);
10298 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10299 // it('should make a JSONP request to angularjs.org', function() {
10300 // sampleJsonpBtn.click();
10301 // fetchBtn.click();
10302 // expect(status.getText()).toMatch('200');
10303 // expect(data.getText()).toMatch(/Super Hero!/);
10306 it('should make JSONP request to invalid URL and invoke the error handler',
10308 invalidJsonpBtn.click();
10310 expect(status.getText()).toMatch('0');
10311 expect(data.getText()).toMatch('Request failed');
10316 function $http(requestConfig) {
10318 if (!angular.isObject(requestConfig)) {
10319 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
10322 var config = extend({
10324 transformRequest: defaults.transformRequest,
10325 transformResponse: defaults.transformResponse,
10326 paramSerializer: defaults.paramSerializer
10329 config.headers = mergeHeaders(requestConfig);
10330 config.method = uppercase(config.method);
10331 config.paramSerializer = isString(config.paramSerializer) ?
10332 $injector.get(config.paramSerializer) : config.paramSerializer;
10334 var serverRequest = function(config) {
10335 var headers = config.headers;
10336 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10338 // strip content-type if data is undefined
10339 if (isUndefined(reqData)) {
10340 forEach(headers, function(value, header) {
10341 if (lowercase(header) === 'content-type') {
10342 delete headers[header];
10347 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10348 config.withCredentials = defaults.withCredentials;
10352 return sendReq(config, reqData).then(transformResponse, transformResponse);
10355 var chain = [serverRequest, undefined];
10356 var promise = $q.when(config);
10358 // apply interceptors
10359 forEach(reversedInterceptors, function(interceptor) {
10360 if (interceptor.request || interceptor.requestError) {
10361 chain.unshift(interceptor.request, interceptor.requestError);
10363 if (interceptor.response || interceptor.responseError) {
10364 chain.push(interceptor.response, interceptor.responseError);
10368 while (chain.length) {
10369 var thenFn = chain.shift();
10370 var rejectFn = chain.shift();
10372 promise = promise.then(thenFn, rejectFn);
10375 if (useLegacyPromise) {
10376 promise.success = function(fn) {
10377 assertArgFn(fn, 'fn');
10379 promise.then(function(response) {
10380 fn(response.data, response.status, response.headers, config);
10385 promise.error = function(fn) {
10386 assertArgFn(fn, 'fn');
10388 promise.then(null, function(response) {
10389 fn(response.data, response.status, response.headers, config);
10394 promise.success = $httpMinErrLegacyFn('success');
10395 promise.error = $httpMinErrLegacyFn('error');
10400 function transformResponse(response) {
10401 // make a copy since the response must be cacheable
10402 var resp = extend({}, response);
10403 resp.data = transformData(response.data, response.headers, response.status,
10404 config.transformResponse);
10405 return (isSuccess(response.status))
10410 function executeHeaderFns(headers, config) {
10411 var headerContent, processedHeaders = {};
10413 forEach(headers, function(headerFn, header) {
10414 if (isFunction(headerFn)) {
10415 headerContent = headerFn(config);
10416 if (headerContent != null) {
10417 processedHeaders[header] = headerContent;
10420 processedHeaders[header] = headerFn;
10424 return processedHeaders;
10427 function mergeHeaders(config) {
10428 var defHeaders = defaults.headers,
10429 reqHeaders = extend({}, config.headers),
10430 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10432 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10434 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10435 defaultHeadersIteration:
10436 for (defHeaderName in defHeaders) {
10437 lowercaseDefHeaderName = lowercase(defHeaderName);
10439 for (reqHeaderName in reqHeaders) {
10440 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10441 continue defaultHeadersIteration;
10445 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10448 // execute if header value is a function for merged headers
10449 return executeHeaderFns(reqHeaders, shallowCopy(config));
10453 $http.pendingRequests = [];
10460 * Shortcut method to perform `GET` request.
10462 * @param {string} url Relative or absolute URL specifying the destination of the request
10463 * @param {Object=} config Optional configuration object
10464 * @returns {HttpPromise} Future object
10469 * @name $http#delete
10472 * Shortcut method to perform `DELETE` request.
10474 * @param {string} url Relative or absolute URL specifying the destination of the request
10475 * @param {Object=} config Optional configuration object
10476 * @returns {HttpPromise} Future object
10484 * Shortcut method to perform `HEAD` request.
10486 * @param {string} url Relative or absolute URL specifying the destination of the request
10487 * @param {Object=} config Optional configuration object
10488 * @returns {HttpPromise} Future object
10493 * @name $http#jsonp
10496 * Shortcut method to perform `JSONP` request.
10498 * @param {string} url Relative or absolute URL specifying the destination of the request.
10499 * The name of the callback should be the string `JSON_CALLBACK`.
10500 * @param {Object=} config Optional configuration object
10501 * @returns {HttpPromise} Future object
10503 createShortMethods('get', 'delete', 'head', 'jsonp');
10510 * Shortcut method to perform `POST` request.
10512 * @param {string} url Relative or absolute URL specifying the destination of the request
10513 * @param {*} data Request content
10514 * @param {Object=} config Optional configuration object
10515 * @returns {HttpPromise} Future object
10523 * Shortcut method to perform `PUT` request.
10525 * @param {string} url Relative or absolute URL specifying the destination of the request
10526 * @param {*} data Request content
10527 * @param {Object=} config Optional configuration object
10528 * @returns {HttpPromise} Future object
10533 * @name $http#patch
10536 * Shortcut method to perform `PATCH` request.
10538 * @param {string} url Relative or absolute URL specifying the destination of the request
10539 * @param {*} data Request content
10540 * @param {Object=} config Optional configuration object
10541 * @returns {HttpPromise} Future object
10543 createShortMethodsWithData('post', 'put', 'patch');
10547 * @name $http#defaults
10550 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10551 * default headers, withCredentials as well as request and response transformations.
10553 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10555 $http.defaults = defaults;
10561 function createShortMethods(names) {
10562 forEach(arguments, function(name) {
10563 $http[name] = function(url, config) {
10564 return $http(extend({}, config || {}, {
10573 function createShortMethodsWithData(name) {
10574 forEach(arguments, function(name) {
10575 $http[name] = function(url, data, config) {
10576 return $http(extend({}, config || {}, {
10587 * Makes the request.
10589 * !!! ACCESSES CLOSURE VARS:
10590 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10592 function sendReq(config, reqData) {
10593 var deferred = $q.defer(),
10594 promise = deferred.promise,
10597 reqHeaders = config.headers,
10598 url = buildUrl(config.url, config.paramSerializer(config.params));
10600 $http.pendingRequests.push(config);
10601 promise.then(removePendingReq, removePendingReq);
10604 if ((config.cache || defaults.cache) && config.cache !== false &&
10605 (config.method === 'GET' || config.method === 'JSONP')) {
10606 cache = isObject(config.cache) ? config.cache
10607 : isObject(defaults.cache) ? defaults.cache
10612 cachedResp = cache.get(url);
10613 if (isDefined(cachedResp)) {
10614 if (isPromiseLike(cachedResp)) {
10615 // cached request has already been sent, but there is no response yet
10616 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10618 // serving from cache
10619 if (isArray(cachedResp)) {
10620 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10622 resolvePromise(cachedResp, 200, {}, 'OK');
10626 // put the promise for the non-transformed response into cache as a placeholder
10627 cache.put(url, promise);
10632 // if we won't have the response in cache, set the xsrf headers and
10633 // send the request to the backend
10634 if (isUndefined(cachedResp)) {
10635 var xsrfValue = urlIsSameOrigin(config.url)
10636 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10639 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10642 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10643 config.withCredentials, config.responseType);
10650 * Callback registered to $httpBackend():
10651 * - caches the response if desired
10652 * - resolves the raw $http promise
10655 function done(status, response, headersString, statusText) {
10657 if (isSuccess(status)) {
10658 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10660 // remove promise from the cache
10665 function resolveHttpPromise() {
10666 resolvePromise(response, status, headersString, statusText);
10669 if (useApplyAsync) {
10670 $rootScope.$applyAsync(resolveHttpPromise);
10672 resolveHttpPromise();
10673 if (!$rootScope.$$phase) $rootScope.$apply();
10679 * Resolves the raw $http promise.
10681 function resolvePromise(response, status, headers, statusText) {
10682 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10683 status = status >= -1 ? status : 0;
10685 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10688 headers: headersGetter(headers),
10690 statusText: statusText
10694 function resolvePromiseWithResult(result) {
10695 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10698 function removePendingReq() {
10699 var idx = $http.pendingRequests.indexOf(config);
10700 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10705 function buildUrl(url, serializedParams) {
10706 if (serializedParams.length > 0) {
10707 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10716 * @name $xhrFactory
10719 * Factory function used to create XMLHttpRequest objects.
10721 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10724 * angular.module('myApp', [])
10725 * .factory('$xhrFactory', function() {
10726 * return function createXhr(method, url) {
10727 * return new window.XMLHttpRequest({mozSystem: true});
10732 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10733 * @param {string} url URL of the request.
10735 function $xhrFactoryProvider() {
10736 this.$get = function() {
10737 return function createXhr() {
10738 return new window.XMLHttpRequest();
10745 * @name $httpBackend
10746 * @requires $window
10747 * @requires $document
10748 * @requires $xhrFactory
10751 * HTTP backend used by the {@link ng.$http service} that delegates to
10752 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10754 * You should never need to use this service directly, instead use the higher-level abstractions:
10755 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10757 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10758 * $httpBackend} which can be trained with responses.
10760 function $HttpBackendProvider() {
10761 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10762 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10766 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10767 // TODO(vojta): fix the signature
10768 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10769 $browser.$$incOutstandingRequestCount();
10770 url = url || $browser.url();
10772 if (lowercase(method) == 'jsonp') {
10773 var callbackId = '_' + (callbacks.counter++).toString(36);
10774 callbacks[callbackId] = function(data) {
10775 callbacks[callbackId].data = data;
10776 callbacks[callbackId].called = true;
10779 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10780 callbackId, function(status, text) {
10781 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10782 callbacks[callbackId] = noop;
10786 var xhr = createXhr(method, url);
10788 xhr.open(method, url, true);
10789 forEach(headers, function(value, key) {
10790 if (isDefined(value)) {
10791 xhr.setRequestHeader(key, value);
10795 xhr.onload = function requestLoaded() {
10796 var statusText = xhr.statusText || '';
10798 // responseText is the old-school way of retrieving response (supported by IE9)
10799 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10800 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10802 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10803 var status = xhr.status === 1223 ? 204 : xhr.status;
10805 // fix status code when it is 0 (0 status is undocumented).
10806 // Occurs when accessing file resources or on Android 4.1 stock browser
10807 // while retrieving files from application cache.
10808 if (status === 0) {
10809 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10812 completeRequest(callback,
10815 xhr.getAllResponseHeaders(),
10819 var requestError = function() {
10820 // The response is always empty
10821 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10822 completeRequest(callback, -1, null, null, '');
10825 xhr.onerror = requestError;
10826 xhr.onabort = requestError;
10828 if (withCredentials) {
10829 xhr.withCredentials = true;
10832 if (responseType) {
10834 xhr.responseType = responseType;
10836 // WebKit added support for the json responseType value on 09/03/2013
10837 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10838 // known to throw when setting the value "json" as the response type. Other older
10839 // browsers implementing the responseType
10841 // The json response type can be ignored if not supported, because JSON payloads are
10842 // parsed on the client-side regardless.
10843 if (responseType !== 'json') {
10849 xhr.send(isUndefined(post) ? null : post);
10853 var timeoutId = $browserDefer(timeoutRequest, timeout);
10854 } else if (isPromiseLike(timeout)) {
10855 timeout.then(timeoutRequest);
10859 function timeoutRequest() {
10860 jsonpDone && jsonpDone();
10861 xhr && xhr.abort();
10864 function completeRequest(callback, status, response, headersString, statusText) {
10865 // cancel timeout and subsequent timeout promise resolution
10866 if (isDefined(timeoutId)) {
10867 $browserDefer.cancel(timeoutId);
10869 jsonpDone = xhr = null;
10871 callback(status, response, headersString, statusText);
10872 $browser.$$completeOutstandingRequest(noop);
10876 function jsonpReq(url, callbackId, done) {
10877 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10878 // - fetches local scripts via XHR and evals them
10879 // - adds and immediately removes script elements from the document
10880 var script = rawDocument.createElement('script'), callback = null;
10881 script.type = "text/javascript";
10883 script.async = true;
10885 callback = function(event) {
10886 removeEventListenerFn(script, "load", callback);
10887 removeEventListenerFn(script, "error", callback);
10888 rawDocument.body.removeChild(script);
10891 var text = "unknown";
10894 if (event.type === "load" && !callbacks[callbackId].called) {
10895 event = { type: "error" };
10898 status = event.type === "error" ? 404 : 200;
10902 done(status, text);
10906 addEventListenerFn(script, "load", callback);
10907 addEventListenerFn(script, "error", callback);
10908 rawDocument.body.appendChild(script);
10913 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10914 $interpolateMinErr.throwNoconcat = function(text) {
10915 throw $interpolateMinErr('noconcat',
10916 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10917 "interpolations that concatenate multiple expressions when a trusted value is " +
10918 "required. See http://docs.angularjs.org/api/ng.$sce", text);
10921 $interpolateMinErr.interr = function(text, err) {
10922 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10927 * @name $interpolateProvider
10931 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10934 <example module="customInterpolationApp">
10935 <file name="index.html">
10937 var customInterpolationApp = angular.module('customInterpolationApp', []);
10939 customInterpolationApp.config(function($interpolateProvider) {
10940 $interpolateProvider.startSymbol('//');
10941 $interpolateProvider.endSymbol('//');
10945 customInterpolationApp.controller('DemoController', function() {
10946 this.label = "This binding is brought you by // interpolation symbols.";
10949 <div ng-app="App" ng-controller="DemoController as demo">
10953 <file name="protractor.js" type="protractor">
10954 it('should interpolate binding with custom symbols', function() {
10955 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10960 function $InterpolateProvider() {
10961 var startSymbol = '{{';
10962 var endSymbol = '}}';
10966 * @name $interpolateProvider#startSymbol
10968 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10970 * @param {string=} value new value to set the starting symbol to.
10971 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10973 this.startSymbol = function(value) {
10975 startSymbol = value;
10978 return startSymbol;
10984 * @name $interpolateProvider#endSymbol
10986 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10988 * @param {string=} value new value to set the ending symbol to.
10989 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10991 this.endSymbol = function(value) {
11001 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11002 var startSymbolLength = startSymbol.length,
11003 endSymbolLength = endSymbol.length,
11004 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11005 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11007 function escape(ch) {
11008 return '\\\\\\' + ch;
11011 function unescapeText(text) {
11012 return text.replace(escapedStartRegexp, startSymbol).
11013 replace(escapedEndRegexp, endSymbol);
11016 function stringify(value) {
11017 if (value == null) { // null || undefined
11020 switch (typeof value) {
11024 value = '' + value;
11027 value = toJson(value);
11035 * @name $interpolate
11043 * Compiles a string with markup into an interpolation function. This service is used by the
11044 * HTML {@link ng.$compile $compile} service for data binding. See
11045 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11046 * interpolation markup.
11050 * var $interpolate = ...; // injected
11051 * var exp = $interpolate('Hello {{name | uppercase}}!');
11052 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11055 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11056 * `true`, the interpolation function will return `undefined` unless all embedded expressions
11057 * evaluate to a value other than `undefined`.
11060 * var $interpolate = ...; // injected
11061 * var context = {greeting: 'Hello', name: undefined };
11063 * // default "forgiving" mode
11064 * var exp = $interpolate('{{greeting}} {{name}}!');
11065 * expect(exp(context)).toEqual('Hello !');
11067 * // "allOrNothing" mode
11068 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11069 * expect(exp(context)).toBeUndefined();
11070 * context.name = 'Angular';
11071 * expect(exp(context)).toEqual('Hello Angular!');
11074 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11076 * ####Escaped Interpolation
11077 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11078 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11079 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11082 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11083 * degree, while also enabling code examples to work without relying on the
11084 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11086 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11087 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
11088 * interpolation start/end markers with their escaped counterparts.**
11090 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11091 * output when the $interpolate service processes the text. So, for HTML elements interpolated
11092 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11093 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11094 * this is typically useful only when user-data is used in rendering a template from the server, or
11095 * when otherwise untrusted data is used by a directive.
11098 * <file name="index.html">
11099 * <div ng-init="username='A user'">
11100 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11102 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
11103 * application, but fails to accomplish their task, because the server has correctly
11104 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11106 * <p>Instead, the result of the attempted script injection is visible, and can be removed
11107 * from the database by an administrator.</p>
11112 * @param {string} text The text with markup to interpolate.
11113 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11114 * embedded expression in order to return an interpolation function. Strings with no
11115 * embedded expression will return null for the interpolation function.
11116 * @param {string=} trustedContext when provided, the returned function passes the interpolated
11117 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11118 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
11119 * provides Strict Contextual Escaping for details.
11120 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11121 * unless all embedded expressions evaluate to a value other than `undefined`.
11122 * @returns {function(context)} an interpolation function which is used to compute the
11123 * interpolated string. The function has these parameters:
11125 * - `context`: evaluation context for all expressions embedded in the interpolated text
11127 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11128 allOrNothing = !!allOrNothing;
11134 textLength = text.length,
11137 expressionPositions = [];
11139 while (index < textLength) {
11140 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11141 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11142 if (index !== startIndex) {
11143 concat.push(unescapeText(text.substring(index, startIndex)));
11145 exp = text.substring(startIndex + startSymbolLength, endIndex);
11146 expressions.push(exp);
11147 parseFns.push($parse(exp, parseStringifyInterceptor));
11148 index = endIndex + endSymbolLength;
11149 expressionPositions.push(concat.length);
11152 // we did not find an interpolation, so we have to add the remainder to the separators array
11153 if (index !== textLength) {
11154 concat.push(unescapeText(text.substring(index)));
11160 // Concatenating expressions makes it hard to reason about whether some combination of
11161 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
11162 // single expression be used for iframe[src], object[src], etc., we ensure that the value
11163 // that's used is assigned or constructed by some JS code somewhere that is more testable or
11164 // make it obvious that you bound the value to some user controlled value. This helps reduce
11165 // the load when auditing for XSS issues.
11166 if (trustedContext && concat.length > 1) {
11167 $interpolateMinErr.throwNoconcat(text);
11170 if (!mustHaveExpression || expressions.length) {
11171 var compute = function(values) {
11172 for (var i = 0, ii = expressions.length; i < ii; i++) {
11173 if (allOrNothing && isUndefined(values[i])) return;
11174 concat[expressionPositions[i]] = values[i];
11176 return concat.join('');
11179 var getValue = function(value) {
11180 return trustedContext ?
11181 $sce.getTrusted(trustedContext, value) :
11182 $sce.valueOf(value);
11185 return extend(function interpolationFn(context) {
11187 var ii = expressions.length;
11188 var values = new Array(ii);
11191 for (; i < ii; i++) {
11192 values[i] = parseFns[i](context);
11195 return compute(values);
11197 $exceptionHandler($interpolateMinErr.interr(text, err));
11201 // all of these properties are undocumented for now
11202 exp: text, //just for compatibility with regular watchers created via $watch
11203 expressions: expressions,
11204 $$watchDelegate: function(scope, listener) {
11206 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11207 var currValue = compute(values);
11208 if (isFunction(listener)) {
11209 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11211 lastValue = currValue;
11217 function parseStringifyInterceptor(value) {
11219 value = getValue(value);
11220 return allOrNothing && !isDefined(value) ? value : stringify(value);
11222 $exceptionHandler($interpolateMinErr.interr(text, err));
11230 * @name $interpolate#startSymbol
11232 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11234 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11237 * @returns {string} start symbol.
11239 $interpolate.startSymbol = function() {
11240 return startSymbol;
11246 * @name $interpolate#endSymbol
11248 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11250 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11253 * @returns {string} end symbol.
11255 $interpolate.endSymbol = function() {
11259 return $interpolate;
11263 function $IntervalProvider() {
11264 this.$get = ['$rootScope', '$window', '$q', '$$q',
11265 function($rootScope, $window, $q, $$q) {
11266 var intervals = {};
11274 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11277 * The return value of registering an interval function is a promise. This promise will be
11278 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11279 * run indefinitely if `count` is not defined. The value of the notification will be the
11280 * number of iterations that have run.
11281 * To cancel an interval, call `$interval.cancel(promise)`.
11283 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11284 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11287 * <div class="alert alert-warning">
11288 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11289 * with them. In particular they are not automatically destroyed when a controller's scope or a
11290 * directive's element are destroyed.
11291 * You should take this into consideration and make sure to always cancel the interval at the
11292 * appropriate moment. See the example below for more details on how and when to do this.
11295 * @param {function()} fn A function that should be called repeatedly.
11296 * @param {number} delay Number of milliseconds between each function call.
11297 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11299 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11300 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11301 * @param {...*=} Pass additional parameters to the executed function.
11302 * @returns {promise} A promise which will be notified on each iteration.
11305 * <example module="intervalExample">
11306 * <file name="index.html">
11308 * angular.module('intervalExample', [])
11309 * .controller('ExampleController', ['$scope', '$interval',
11310 * function($scope, $interval) {
11311 * $scope.format = 'M/d/yy h:mm:ss a';
11312 * $scope.blood_1 = 100;
11313 * $scope.blood_2 = 120;
11316 * $scope.fight = function() {
11317 * // Don't start a new fight if we are already fighting
11318 * if ( angular.isDefined(stop) ) return;
11320 * stop = $interval(function() {
11321 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11322 * $scope.blood_1 = $scope.blood_1 - 3;
11323 * $scope.blood_2 = $scope.blood_2 - 4;
11325 * $scope.stopFight();
11330 * $scope.stopFight = function() {
11331 * if (angular.isDefined(stop)) {
11332 * $interval.cancel(stop);
11333 * stop = undefined;
11337 * $scope.resetFight = function() {
11338 * $scope.blood_1 = 100;
11339 * $scope.blood_2 = 120;
11342 * $scope.$on('$destroy', function() {
11343 * // Make sure that the interval is destroyed too
11344 * $scope.stopFight();
11347 * // Register the 'myCurrentTime' directive factory method.
11348 * // We inject $interval and dateFilter service since the factory method is DI.
11349 * .directive('myCurrentTime', ['$interval', 'dateFilter',
11350 * function($interval, dateFilter) {
11351 * // return the directive link function. (compile function not needed)
11352 * return function(scope, element, attrs) {
11353 * var format, // date format
11354 * stopTime; // so that we can cancel the time updates
11356 * // used to update the UI
11357 * function updateTime() {
11358 * element.text(dateFilter(new Date(), format));
11361 * // watch the expression, and update the UI on change.
11362 * scope.$watch(attrs.myCurrentTime, function(value) {
11367 * stopTime = $interval(updateTime, 1000);
11369 * // listen on DOM destroy (removal) event, and cancel the next UI update
11370 * // to prevent updating time after the DOM element was removed.
11371 * element.on('$destroy', function() {
11372 * $interval.cancel(stopTime);
11379 * <div ng-controller="ExampleController">
11380 * <label>Date format: <input ng-model="format"></label> <hr/>
11381 * Current time is: <span my-current-time="format"></span>
11383 * Blood 1 : <font color='red'>{{blood_1}}</font>
11384 * Blood 2 : <font color='red'>{{blood_2}}</font>
11385 * <button type="button" data-ng-click="fight()">Fight</button>
11386 * <button type="button" data-ng-click="stopFight()">StopFight</button>
11387 * <button type="button" data-ng-click="resetFight()">resetFight</button>
11394 function interval(fn, delay, count, invokeApply) {
11395 var hasParams = arguments.length > 4,
11396 args = hasParams ? sliceArgs(arguments, 4) : [],
11397 setInterval = $window.setInterval,
11398 clearInterval = $window.clearInterval,
11400 skipApply = (isDefined(invokeApply) && !invokeApply),
11401 deferred = (skipApply ? $$q : $q).defer(),
11402 promise = deferred.promise;
11404 count = isDefined(count) ? count : 0;
11406 promise.then(null, null, (!hasParams) ? fn : function() {
11407 fn.apply(null, args);
11410 promise.$$intervalId = setInterval(function tick() {
11411 deferred.notify(iteration++);
11413 if (count > 0 && iteration >= count) {
11414 deferred.resolve(iteration);
11415 clearInterval(promise.$$intervalId);
11416 delete intervals[promise.$$intervalId];
11419 if (!skipApply) $rootScope.$apply();
11423 intervals[promise.$$intervalId] = deferred;
11431 * @name $interval#cancel
11434 * Cancels a task associated with the `promise`.
11436 * @param {Promise=} promise returned by the `$interval` function.
11437 * @returns {boolean} Returns `true` if the task was successfully canceled.
11439 interval.cancel = function(promise) {
11440 if (promise && promise.$$intervalId in intervals) {
11441 intervals[promise.$$intervalId].reject('canceled');
11442 $window.clearInterval(promise.$$intervalId);
11443 delete intervals[promise.$$intervalId];
11458 * $locale service provides localization rules for various Angular components. As of right now the
11459 * only public api is:
11461 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11464 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11465 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11466 var $locationMinErr = minErr('$location');
11470 * Encode path using encodeUriSegment, ignoring forward slashes
11472 * @param {string} path Path to encode
11473 * @returns {string}
11475 function encodePath(path) {
11476 var segments = path.split('/'),
11477 i = segments.length;
11480 segments[i] = encodeUriSegment(segments[i]);
11483 return segments.join('/');
11486 function parseAbsoluteUrl(absoluteUrl, locationObj) {
11487 var parsedUrl = urlResolve(absoluteUrl);
11489 locationObj.$$protocol = parsedUrl.protocol;
11490 locationObj.$$host = parsedUrl.hostname;
11491 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11495 function parseAppUrl(relativeUrl, locationObj) {
11496 var prefixed = (relativeUrl.charAt(0) !== '/');
11498 relativeUrl = '/' + relativeUrl;
11500 var match = urlResolve(relativeUrl);
11501 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11502 match.pathname.substring(1) : match.pathname);
11503 locationObj.$$search = parseKeyValue(match.search);
11504 locationObj.$$hash = decodeURIComponent(match.hash);
11506 // make sure path starts with '/';
11507 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11508 locationObj.$$path = '/' + locationObj.$$path;
11515 * @param {string} begin
11516 * @param {string} whole
11517 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11520 function beginsWith(begin, whole) {
11521 if (whole.indexOf(begin) === 0) {
11522 return whole.substr(begin.length);
11527 function stripHash(url) {
11528 var index = url.indexOf('#');
11529 return index == -1 ? url : url.substr(0, index);
11532 function trimEmptyHash(url) {
11533 return url.replace(/(#.+)|#$/, '$1');
11537 function stripFile(url) {
11538 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11541 /* return the server only (scheme://host:port) */
11542 function serverBase(url) {
11543 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11548 * LocationHtml5Url represents an url
11549 * This object is exposed as $location service when HTML5 mode is enabled and supported
11552 * @param {string} appBase application base URL
11553 * @param {string} appBaseNoFile application base URL stripped of any filename
11554 * @param {string} basePrefix url path prefix
11556 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11557 this.$$html5 = true;
11558 basePrefix = basePrefix || '';
11559 parseAbsoluteUrl(appBase, this);
11563 * Parse given html5 (regular) url string into properties
11564 * @param {string} url HTML5 url
11567 this.$$parse = function(url) {
11568 var pathUrl = beginsWith(appBaseNoFile, url);
11569 if (!isString(pathUrl)) {
11570 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11574 parseAppUrl(pathUrl, this);
11576 if (!this.$$path) {
11584 * Compose url and update `absUrl` property
11587 this.$$compose = function() {
11588 var search = toKeyValue(this.$$search),
11589 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11591 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11592 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11595 this.$$parseLinkUrl = function(url, relHref) {
11596 if (relHref && relHref[0] === '#') {
11597 // special case for links to hash fragments:
11598 // keep the old url and only replace the hash fragment
11599 this.hash(relHref.slice(1));
11602 var appUrl, prevAppUrl;
11605 if (isDefined(appUrl = beginsWith(appBase, url))) {
11606 prevAppUrl = appUrl;
11607 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11608 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11610 rewrittenUrl = appBase + prevAppUrl;
11612 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11613 rewrittenUrl = appBaseNoFile + appUrl;
11614 } else if (appBaseNoFile == url + '/') {
11615 rewrittenUrl = appBaseNoFile;
11617 if (rewrittenUrl) {
11618 this.$$parse(rewrittenUrl);
11620 return !!rewrittenUrl;
11626 * LocationHashbangUrl represents url
11627 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11628 * It also serves as the base class for html5 mode fallback on legacy browsers.
11631 * @param {string} appBase application base URL
11632 * @param {string} appBaseNoFile application base URL stripped of any filename
11633 * @param {string} hashPrefix hashbang prefix
11635 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11637 parseAbsoluteUrl(appBase, this);
11641 * Parse given hashbang url into properties
11642 * @param {string} url Hashbang url
11645 this.$$parse = function(url) {
11646 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11647 var withoutHashUrl;
11649 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11651 // The rest of the url starts with a hash so we have
11652 // got either a hashbang path or a plain hash fragment
11653 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11654 if (isUndefined(withoutHashUrl)) {
11655 // There was no hashbang prefix so we just have a hash fragment
11656 withoutHashUrl = withoutBaseUrl;
11660 // There was no hashbang path nor hash fragment:
11661 // If we are in HTML5 mode we use what is left as the path;
11662 // Otherwise we ignore what is left
11663 if (this.$$html5) {
11664 withoutHashUrl = withoutBaseUrl;
11666 withoutHashUrl = '';
11667 if (isUndefined(withoutBaseUrl)) {
11674 parseAppUrl(withoutHashUrl, this);
11676 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11681 * In Windows, on an anchor node on documents loaded from
11682 * the filesystem, the browser will return a pathname
11683 * prefixed with the drive name ('/C:/path') when a
11684 * pathname without a drive is set:
11685 * * a.setAttribute('href', '/foo')
11686 * * a.pathname === '/C:/foo' //true
11688 * Inside of Angular, we're always using pathnames that
11689 * do not include drive names for routing.
11691 function removeWindowsDriveName(path, url, base) {
11693 Matches paths for file protocol on windows,
11694 such as /C:/foo/bar, and captures only /foo/bar.
11696 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11698 var firstPathSegmentMatch;
11700 //Get the relative path from the input URL.
11701 if (url.indexOf(base) === 0) {
11702 url = url.replace(base, '');
11705 // The input URL intentionally contains a first path segment that ends with a colon.
11706 if (windowsFilePathExp.exec(url)) {
11710 firstPathSegmentMatch = windowsFilePathExp.exec(path);
11711 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11716 * Compose hashbang url and update `absUrl` property
11719 this.$$compose = function() {
11720 var search = toKeyValue(this.$$search),
11721 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11723 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11724 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11727 this.$$parseLinkUrl = function(url, relHref) {
11728 if (stripHash(appBase) == stripHash(url)) {
11738 * LocationHashbangUrl represents url
11739 * This object is exposed as $location service when html5 history api is enabled but the browser
11740 * does not support it.
11743 * @param {string} appBase application base URL
11744 * @param {string} appBaseNoFile application base URL stripped of any filename
11745 * @param {string} hashPrefix hashbang prefix
11747 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11748 this.$$html5 = true;
11749 LocationHashbangUrl.apply(this, arguments);
11751 this.$$parseLinkUrl = function(url, relHref) {
11752 if (relHref && relHref[0] === '#') {
11753 // special case for links to hash fragments:
11754 // keep the old url and only replace the hash fragment
11755 this.hash(relHref.slice(1));
11762 if (appBase == stripHash(url)) {
11763 rewrittenUrl = url;
11764 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11765 rewrittenUrl = appBase + hashPrefix + appUrl;
11766 } else if (appBaseNoFile === url + '/') {
11767 rewrittenUrl = appBaseNoFile;
11769 if (rewrittenUrl) {
11770 this.$$parse(rewrittenUrl);
11772 return !!rewrittenUrl;
11775 this.$$compose = function() {
11776 var search = toKeyValue(this.$$search),
11777 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11779 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11780 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11781 this.$$absUrl = appBase + hashPrefix + this.$$url;
11787 var locationPrototype = {
11790 * Are we in html5 mode?
11796 * Has any change been replacing?
11803 * @name $location#absUrl
11806 * This method is getter only.
11808 * Return full url representation with all segments encoded according to rules specified in
11809 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11813 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11814 * var absUrl = $location.absUrl();
11815 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11818 * @return {string} full url
11820 absUrl: locationGetter('$$absUrl'),
11824 * @name $location#url
11827 * This method is getter / setter.
11829 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11831 * Change path, search and hash, when called with parameter and return `$location`.
11835 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11836 * var url = $location.url();
11837 * // => "/some/path?foo=bar&baz=xoxo"
11840 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11841 * @return {string} url
11843 url: function(url) {
11844 if (isUndefined(url)) {
11848 var match = PATH_MATCH.exec(url);
11849 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11850 if (match[2] || match[1] || url === '') this.search(match[3] || '');
11851 this.hash(match[5] || '');
11858 * @name $location#protocol
11861 * This method is getter only.
11863 * Return protocol of current url.
11867 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11868 * var protocol = $location.protocol();
11872 * @return {string} protocol of current url
11874 protocol: locationGetter('$$protocol'),
11878 * @name $location#host
11881 * This method is getter only.
11883 * Return host of current url.
11885 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11889 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11890 * var host = $location.host();
11891 * // => "example.com"
11893 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11894 * host = $location.host();
11895 * // => "example.com"
11896 * host = location.host;
11897 * // => "example.com:8080"
11900 * @return {string} host of current url.
11902 host: locationGetter('$$host'),
11906 * @name $location#port
11909 * This method is getter only.
11911 * Return port of current url.
11915 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11916 * var port = $location.port();
11920 * @return {Number} port
11922 port: locationGetter('$$port'),
11926 * @name $location#path
11929 * This method is getter / setter.
11931 * Return path of current url when called without any parameter.
11933 * Change path when called with parameter and return `$location`.
11935 * Note: Path should always begin with forward slash (/), this method will add the forward slash
11936 * if it is missing.
11940 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11941 * var path = $location.path();
11942 * // => "/some/path"
11945 * @param {(string|number)=} path New path
11946 * @return {string} path
11948 path: locationGetterSetter('$$path', function(path) {
11949 path = path !== null ? path.toString() : '';
11950 return path.charAt(0) == '/' ? path : '/' + path;
11955 * @name $location#search
11958 * This method is getter / setter.
11960 * Return search part (as object) of current url when called without any parameter.
11962 * Change search part when called with parameter and return `$location`.
11966 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11967 * var searchObject = $location.search();
11968 * // => {foo: 'bar', baz: 'xoxo'}
11970 * // set foo to 'yipee'
11971 * $location.search('foo', 'yipee');
11972 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11975 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11978 * When called with a single argument the method acts as a setter, setting the `search` component
11979 * of `$location` to the specified value.
11981 * If the argument is a hash object containing an array of values, these values will be encoded
11982 * as duplicate search parameters in the url.
11984 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11985 * will override only a single search property.
11987 * If `paramValue` is an array, it will override the property of the `search` component of
11988 * `$location` specified via the first argument.
11990 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11992 * If `paramValue` is `true`, the property specified via the first argument will be added with no
11993 * value nor trailing equal sign.
11995 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11996 * one or more arguments returns `$location` object itself.
11998 search: function(search, paramValue) {
11999 switch (arguments.length) {
12001 return this.$$search;
12003 if (isString(search) || isNumber(search)) {
12004 search = search.toString();
12005 this.$$search = parseKeyValue(search);
12006 } else if (isObject(search)) {
12007 search = copy(search, {});
12008 // remove object undefined or null properties
12009 forEach(search, function(value, key) {
12010 if (value == null) delete search[key];
12013 this.$$search = search;
12015 throw $locationMinErr('isrcharg',
12016 'The first argument of the `$location#search()` call must be a string or an object.');
12020 if (isUndefined(paramValue) || paramValue === null) {
12021 delete this.$$search[search];
12023 this.$$search[search] = paramValue;
12033 * @name $location#hash
12036 * This method is getter / setter.
12038 * Returns the hash fragment when called without any parameters.
12040 * Changes the hash fragment when called with a parameter and returns `$location`.
12044 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12045 * var hash = $location.hash();
12046 * // => "hashValue"
12049 * @param {(string|number)=} hash New hash fragment
12050 * @return {string} hash
12052 hash: locationGetterSetter('$$hash', function(hash) {
12053 return hash !== null ? hash.toString() : '';
12058 * @name $location#replace
12061 * If called, all changes to $location during the current `$digest` will replace the current history
12062 * record, instead of adding a new one.
12064 replace: function() {
12065 this.$$replace = true;
12070 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12071 Location.prototype = Object.create(locationPrototype);
12075 * @name $location#state
12078 * This method is getter / setter.
12080 * Return the history state object when called without any parameter.
12082 * Change the history state object when called with one parameter and return `$location`.
12083 * The state object is later passed to `pushState` or `replaceState`.
12085 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12086 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12087 * older browsers (like IE9 or Android < 4.0), don't use this method.
12089 * @param {object=} state State object for pushState or replaceState
12090 * @return {object} state
12092 Location.prototype.state = function(state) {
12093 if (!arguments.length) {
12094 return this.$$state;
12097 if (Location !== LocationHtml5Url || !this.$$html5) {
12098 throw $locationMinErr('nostate', 'History API state support is available only ' +
12099 'in HTML5 mode and only in browsers supporting HTML5 History API');
12101 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12102 // but we're changing the $$state reference to $browser.state() during the $digest
12103 // so the modification window is narrow.
12104 this.$$state = isUndefined(state) ? null : state;
12111 function locationGetter(property) {
12112 return function() {
12113 return this[property];
12118 function locationGetterSetter(property, preprocess) {
12119 return function(value) {
12120 if (isUndefined(value)) {
12121 return this[property];
12124 this[property] = preprocess(value);
12136 * @requires $rootElement
12139 * The $location service parses the URL in the browser address bar (based on the
12140 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12141 * available to your application. Changes to the URL in the address bar are reflected into
12142 * $location service and changes to $location are reflected into the browser address bar.
12144 * **The $location service:**
12146 * - Exposes the current URL in the browser address bar, so you can
12147 * - Watch and observe the URL.
12148 * - Change the URL.
12149 * - Synchronizes the URL with the browser when the user
12150 * - Changes the address bar.
12151 * - Clicks the back or forward button (or clicks a History link).
12152 * - Clicks on a link.
12153 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12155 * For more information see {@link guide/$location Developer Guide: Using $location}
12160 * @name $locationProvider
12162 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12164 function $LocationProvider() {
12165 var hashPrefix = '',
12174 * @name $locationProvider#hashPrefix
12176 * @param {string=} prefix Prefix for hash part (containing path and search)
12177 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12179 this.hashPrefix = function(prefix) {
12180 if (isDefined(prefix)) {
12181 hashPrefix = prefix;
12190 * @name $locationProvider#html5Mode
12192 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12193 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12195 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12196 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12197 * support `pushState`.
12198 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12199 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12200 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
12201 * See the {@link guide/$location $location guide for more information}
12202 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12203 * enables/disables url rewriting for relative links.
12205 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12207 this.html5Mode = function(mode) {
12208 if (isBoolean(mode)) {
12209 html5Mode.enabled = mode;
12211 } else if (isObject(mode)) {
12213 if (isBoolean(mode.enabled)) {
12214 html5Mode.enabled = mode.enabled;
12217 if (isBoolean(mode.requireBase)) {
12218 html5Mode.requireBase = mode.requireBase;
12221 if (isBoolean(mode.rewriteLinks)) {
12222 html5Mode.rewriteLinks = mode.rewriteLinks;
12233 * @name $location#$locationChangeStart
12234 * @eventType broadcast on root scope
12236 * Broadcasted before a URL will change.
12238 * This change can be prevented by calling
12239 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12240 * details about event object. Upon successful change
12241 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12243 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12244 * the browser supports the HTML5 History API.
12246 * @param {Object} angularEvent Synthetic event object.
12247 * @param {string} newUrl New URL
12248 * @param {string=} oldUrl URL that was before it was changed.
12249 * @param {string=} newState New history state object
12250 * @param {string=} oldState History state object that was before it was changed.
12255 * @name $location#$locationChangeSuccess
12256 * @eventType broadcast on root scope
12258 * Broadcasted after a URL was changed.
12260 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12261 * the browser supports the HTML5 History API.
12263 * @param {Object} angularEvent Synthetic event object.
12264 * @param {string} newUrl New URL
12265 * @param {string=} oldUrl URL that was before it was changed.
12266 * @param {string=} newState New history state object
12267 * @param {string=} oldState History state object that was before it was changed.
12270 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12271 function($rootScope, $browser, $sniffer, $rootElement, $window) {
12274 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12275 initialUrl = $browser.url(),
12278 if (html5Mode.enabled) {
12279 if (!baseHref && html5Mode.requireBase) {
12280 throw $locationMinErr('nobase',
12281 "$location in HTML5 mode requires a <base> tag to be present!");
12283 appBase = serverBase(initialUrl) + (baseHref || '/');
12284 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12286 appBase = stripHash(initialUrl);
12287 LocationMode = LocationHashbangUrl;
12289 var appBaseNoFile = stripFile(appBase);
12291 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12292 $location.$$parseLinkUrl(initialUrl, initialUrl);
12294 $location.$$state = $browser.state();
12296 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12298 function setBrowserUrlWithFallback(url, replace, state) {
12299 var oldUrl = $location.url();
12300 var oldState = $location.$$state;
12302 $browser.url(url, replace, state);
12304 // Make sure $location.state() returns referentially identical (not just deeply equal)
12305 // state object; this makes possible quick checking if the state changed in the digest
12306 // loop. Checking deep equality would be too expensive.
12307 $location.$$state = $browser.state();
12309 // Restore old values if pushState fails
12310 $location.url(oldUrl);
12311 $location.$$state = oldState;
12317 $rootElement.on('click', function(event) {
12318 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12319 // currently we open nice url link and redirect then
12321 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12323 var elm = jqLite(event.target);
12325 // traverse the DOM up to find first A tag
12326 while (nodeName_(elm[0]) !== 'a') {
12327 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12328 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12331 var absHref = elm.prop('href');
12332 // get the actual href attribute - see
12333 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12334 var relHref = elm.attr('href') || elm.attr('xlink:href');
12336 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12337 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12339 absHref = urlResolve(absHref.animVal).href;
12342 // Ignore when url is started with javascript: or mailto:
12343 if (IGNORE_URI_REGEXP.test(absHref)) return;
12345 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12346 if ($location.$$parseLinkUrl(absHref, relHref)) {
12347 // We do a preventDefault for all urls that are part of the angular application,
12348 // in html5mode and also without, so that we are able to abort navigation without
12349 // getting double entries in the location history.
12350 event.preventDefault();
12351 // update location manually
12352 if ($location.absUrl() != $browser.url()) {
12353 $rootScope.$apply();
12354 // hack to work around FF6 bug 684208 when scenario runner clicks on links
12355 $window.angular['ff-684208-preventDefault'] = true;
12362 // rewrite hashbang url <> html5 url
12363 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12364 $browser.url($location.absUrl(), true);
12367 var initializing = true;
12369 // update $location when $browser url changes
12370 $browser.onUrlChange(function(newUrl, newState) {
12372 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12373 // If we are navigating outside of the app then force a reload
12374 $window.location.href = newUrl;
12378 $rootScope.$evalAsync(function() {
12379 var oldUrl = $location.absUrl();
12380 var oldState = $location.$$state;
12381 var defaultPrevented;
12382 newUrl = trimEmptyHash(newUrl);
12383 $location.$$parse(newUrl);
12384 $location.$$state = newState;
12386 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12387 newState, oldState).defaultPrevented;
12389 // if the location was changed by a `$locationChangeStart` handler then stop
12390 // processing this location change
12391 if ($location.absUrl() !== newUrl) return;
12393 if (defaultPrevented) {
12394 $location.$$parse(oldUrl);
12395 $location.$$state = oldState;
12396 setBrowserUrlWithFallback(oldUrl, false, oldState);
12398 initializing = false;
12399 afterLocationChange(oldUrl, oldState);
12402 if (!$rootScope.$$phase) $rootScope.$digest();
12406 $rootScope.$watch(function $locationWatch() {
12407 var oldUrl = trimEmptyHash($browser.url());
12408 var newUrl = trimEmptyHash($location.absUrl());
12409 var oldState = $browser.state();
12410 var currentReplace = $location.$$replace;
12411 var urlOrStateChanged = oldUrl !== newUrl ||
12412 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12414 if (initializing || urlOrStateChanged) {
12415 initializing = false;
12417 $rootScope.$evalAsync(function() {
12418 var newUrl = $location.absUrl();
12419 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12420 $location.$$state, oldState).defaultPrevented;
12422 // if the location was changed by a `$locationChangeStart` handler then stop
12423 // processing this location change
12424 if ($location.absUrl() !== newUrl) return;
12426 if (defaultPrevented) {
12427 $location.$$parse(oldUrl);
12428 $location.$$state = oldState;
12430 if (urlOrStateChanged) {
12431 setBrowserUrlWithFallback(newUrl, currentReplace,
12432 oldState === $location.$$state ? null : $location.$$state);
12434 afterLocationChange(oldUrl, oldState);
12439 $location.$$replace = false;
12441 // we don't need to return anything because $evalAsync will make the digest loop dirty when
12442 // there is a change
12447 function afterLocationChange(oldUrl, oldState) {
12448 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12449 $location.$$state, oldState);
12457 * @requires $window
12460 * Simple service for logging. Default implementation safely writes the message
12461 * into the browser's console (if present).
12463 * The main purpose of this service is to simplify debugging and troubleshooting.
12465 * The default is to log `debug` messages. You can use
12466 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12469 <example module="logExample">
12470 <file name="script.js">
12471 angular.module('logExample', [])
12472 .controller('LogController', ['$scope', '$log', function($scope, $log) {
12473 $scope.$log = $log;
12474 $scope.message = 'Hello World!';
12477 <file name="index.html">
12478 <div ng-controller="LogController">
12479 <p>Reload this page with open console, enter text and hit the log button...</p>
12481 <input type="text" ng-model="message" /></label>
12482 <button ng-click="$log.log(message)">log</button>
12483 <button ng-click="$log.warn(message)">warn</button>
12484 <button ng-click="$log.info(message)">info</button>
12485 <button ng-click="$log.error(message)">error</button>
12486 <button ng-click="$log.debug(message)">debug</button>
12494 * @name $logProvider
12496 * Use the `$logProvider` to configure how the application logs messages
12498 function $LogProvider() {
12504 * @name $logProvider#debugEnabled
12506 * @param {boolean=} flag enable or disable debug level messages
12507 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12509 this.debugEnabled = function(flag) {
12510 if (isDefined(flag)) {
12518 this.$get = ['$window', function($window) {
12525 * Write a log message
12527 log: consoleLog('log'),
12534 * Write an information message
12536 info: consoleLog('info'),
12543 * Write a warning message
12545 warn: consoleLog('warn'),
12552 * Write an error message
12554 error: consoleLog('error'),
12561 * Write a debug message
12563 debug: (function() {
12564 var fn = consoleLog('debug');
12566 return function() {
12568 fn.apply(self, arguments);
12574 function formatError(arg) {
12575 if (arg instanceof Error) {
12577 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12578 ? 'Error: ' + arg.message + '\n' + arg.stack
12580 } else if (arg.sourceURL) {
12581 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12587 function consoleLog(type) {
12588 var console = $window.console || {},
12589 logFn = console[type] || console.log || noop,
12592 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12593 // The reason behind this is that console.log has type "object" in IE8...
12595 hasApply = !!logFn.apply;
12599 return function() {
12601 forEach(arguments, function(arg) {
12602 args.push(formatError(arg));
12604 return logFn.apply(console, args);
12608 // we are IE which either doesn't have window.console => this is noop and we do nothing,
12609 // or we are IE where console.log doesn't have apply so we log at least first 2 args
12610 return function(arg1, arg2) {
12611 logFn(arg1, arg2 == null ? '' : arg2);
12617 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12618 * Any commits to this file should be reviewed with security in mind. *
12619 * Changes to this file can potentially create security vulnerabilities. *
12620 * An approval from 2 Core members with history of modifying *
12621 * this file is required. *
12623 * Does the change somehow allow for arbitrary javascript to be executed? *
12624 * Or allows for someone to change the prototype of built-in objects? *
12625 * Or gives undesired access to variables likes document or window? *
12626 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12628 var $parseMinErr = minErr('$parse');
12630 // Sandboxing Angular Expressions
12631 // ------------------------------
12632 // Angular expressions are generally considered safe because these expressions only have direct
12633 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12634 // obtaining a reference to native JS functions such as the Function constructor.
12636 // As an example, consider the following Angular expression:
12638 // {}.toString.constructor('alert("evil JS code")')
12640 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12641 // against the expression language, but not to prevent exploits that were enabled by exposing
12642 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12643 // practice and therefore we are not even trying to protect against interaction with an object
12644 // explicitly exposed in this way.
12646 // In general, it is not possible to access a Window object from an angular expression unless a
12647 // window or some DOM object that has a reference to window is published onto a Scope.
12648 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12651 // See https://docs.angularjs.org/guide/security
12654 function ensureSafeMemberName(name, fullExpression) {
12655 if (name === "__defineGetter__" || name === "__defineSetter__"
12656 || name === "__lookupGetter__" || name === "__lookupSetter__"
12657 || name === "__proto__") {
12658 throw $parseMinErr('isecfld',
12659 'Attempting to access a disallowed field in Angular expressions! '
12660 + 'Expression: {0}', fullExpression);
12665 function getStringValue(name, fullExpression) {
12666 // From the JavaScript docs:
12667 // Property names must be strings. This means that non-string objects cannot be used
12668 // as keys in an object. Any non-string object, including a number, is typecasted
12669 // into a string via the toString method.
12671 // So, to ensure that we are checking the same `name` that JavaScript would use,
12672 // we cast it to a string, if possible.
12673 // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12674 // this is, this will handle objects that misbehave.
12676 if (!isString(name)) {
12677 throw $parseMinErr('iseccst',
12678 'Cannot convert object to primitive value! '
12679 + 'Expression: {0}', fullExpression);
12684 function ensureSafeObject(obj, fullExpression) {
12685 // nifty check if obj is Function that is fast and works across iframes and other contexts
12687 if (obj.constructor === obj) {
12688 throw $parseMinErr('isecfn',
12689 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12691 } else if (// isWindow(obj)
12692 obj.window === obj) {
12693 throw $parseMinErr('isecwindow',
12694 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12696 } else if (// isElement(obj)
12697 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12698 throw $parseMinErr('isecdom',
12699 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12701 } else if (// block Object so that we can't get hold of dangerous Object.* methods
12703 throw $parseMinErr('isecobj',
12704 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12711 var CALL = Function.prototype.call;
12712 var APPLY = Function.prototype.apply;
12713 var BIND = Function.prototype.bind;
12715 function ensureSafeFunction(obj, fullExpression) {
12717 if (obj.constructor === obj) {
12718 throw $parseMinErr('isecfn',
12719 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12721 } else if (obj === CALL || obj === APPLY || obj === BIND) {
12722 throw $parseMinErr('isecff',
12723 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12729 function ensureSafeAssignContext(obj, fullExpression) {
12731 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12732 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12733 throw $parseMinErr('isecaf',
12734 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12739 var OPERATORS = createMap();
12740 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12741 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12744 /////////////////////////////////////////
12750 var Lexer = function(options) {
12751 this.options = options;
12754 Lexer.prototype = {
12755 constructor: Lexer,
12757 lex: function(text) {
12762 while (this.index < this.text.length) {
12763 var ch = this.text.charAt(this.index);
12764 if (ch === '"' || ch === "'") {
12765 this.readString(ch);
12766 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12768 } else if (this.isIdent(ch)) {
12770 } else if (this.is(ch, '(){}[].,;:?')) {
12771 this.tokens.push({index: this.index, text: ch});
12773 } else if (this.isWhitespace(ch)) {
12776 var ch2 = ch + this.peek();
12777 var ch3 = ch2 + this.peek(2);
12778 var op1 = OPERATORS[ch];
12779 var op2 = OPERATORS[ch2];
12780 var op3 = OPERATORS[ch3];
12781 if (op1 || op2 || op3) {
12782 var token = op3 ? ch3 : (op2 ? ch2 : ch);
12783 this.tokens.push({index: this.index, text: token, operator: true});
12784 this.index += token.length;
12786 this.throwError('Unexpected next character ', this.index, this.index + 1);
12790 return this.tokens;
12793 is: function(ch, chars) {
12794 return chars.indexOf(ch) !== -1;
12797 peek: function(i) {
12799 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12802 isNumber: function(ch) {
12803 return ('0' <= ch && ch <= '9') && typeof ch === "string";
12806 isWhitespace: function(ch) {
12807 // IE treats non-breaking space as \u00A0
12808 return (ch === ' ' || ch === '\r' || ch === '\t' ||
12809 ch === '\n' || ch === '\v' || ch === '\u00A0');
12812 isIdent: function(ch) {
12813 return ('a' <= ch && ch <= 'z' ||
12814 'A' <= ch && ch <= 'Z' ||
12815 '_' === ch || ch === '$');
12818 isExpOperator: function(ch) {
12819 return (ch === '-' || ch === '+' || this.isNumber(ch));
12822 throwError: function(error, start, end) {
12823 end = end || this.index;
12824 var colStr = (isDefined(start)
12825 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12827 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12828 error, colStr, this.text);
12831 readNumber: function() {
12833 var start = this.index;
12834 while (this.index < this.text.length) {
12835 var ch = lowercase(this.text.charAt(this.index));
12836 if (ch == '.' || this.isNumber(ch)) {
12839 var peekCh = this.peek();
12840 if (ch == 'e' && this.isExpOperator(peekCh)) {
12842 } else if (this.isExpOperator(ch) &&
12843 peekCh && this.isNumber(peekCh) &&
12844 number.charAt(number.length - 1) == 'e') {
12846 } else if (this.isExpOperator(ch) &&
12847 (!peekCh || !this.isNumber(peekCh)) &&
12848 number.charAt(number.length - 1) == 'e') {
12849 this.throwError('Invalid exponent');
12860 value: Number(number)
12864 readIdent: function() {
12865 var start = this.index;
12866 while (this.index < this.text.length) {
12867 var ch = this.text.charAt(this.index);
12868 if (!(this.isIdent(ch) || this.isNumber(ch))) {
12875 text: this.text.slice(start, this.index),
12880 readString: function(quote) {
12881 var start = this.index;
12884 var rawString = quote;
12885 var escape = false;
12886 while (this.index < this.text.length) {
12887 var ch = this.text.charAt(this.index);
12891 var hex = this.text.substring(this.index + 1, this.index + 5);
12892 if (!hex.match(/[\da-f]{4}/i)) {
12893 this.throwError('Invalid unicode escape [\\u' + hex + ']');
12896 string += String.fromCharCode(parseInt(hex, 16));
12898 var rep = ESCAPE[ch];
12899 string = string + (rep || ch);
12902 } else if (ch === '\\') {
12904 } else if (ch === quote) {
12918 this.throwError('Unterminated quote', start);
12922 var AST = function(lexer, options) {
12923 this.lexer = lexer;
12924 this.options = options;
12927 AST.Program = 'Program';
12928 AST.ExpressionStatement = 'ExpressionStatement';
12929 AST.AssignmentExpression = 'AssignmentExpression';
12930 AST.ConditionalExpression = 'ConditionalExpression';
12931 AST.LogicalExpression = 'LogicalExpression';
12932 AST.BinaryExpression = 'BinaryExpression';
12933 AST.UnaryExpression = 'UnaryExpression';
12934 AST.CallExpression = 'CallExpression';
12935 AST.MemberExpression = 'MemberExpression';
12936 AST.Identifier = 'Identifier';
12937 AST.Literal = 'Literal';
12938 AST.ArrayExpression = 'ArrayExpression';
12939 AST.Property = 'Property';
12940 AST.ObjectExpression = 'ObjectExpression';
12941 AST.ThisExpression = 'ThisExpression';
12943 // Internal use only
12944 AST.NGValueParameter = 'NGValueParameter';
12947 ast: function(text) {
12949 this.tokens = this.lexer.lex(text);
12951 var value = this.program();
12953 if (this.tokens.length !== 0) {
12954 this.throwError('is an unexpected token', this.tokens[0]);
12960 program: function() {
12963 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12964 body.push(this.expressionStatement());
12965 if (!this.expect(';')) {
12966 return { type: AST.Program, body: body};
12971 expressionStatement: function() {
12972 return { type: AST.ExpressionStatement, expression: this.filterChain() };
12975 filterChain: function() {
12976 var left = this.expression();
12978 while ((token = this.expect('|'))) {
12979 left = this.filter(left);
12984 expression: function() {
12985 return this.assignment();
12988 assignment: function() {
12989 var result = this.ternary();
12990 if (this.expect('=')) {
12991 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12996 ternary: function() {
12997 var test = this.logicalOR();
13000 if (this.expect('?')) {
13001 alternate = this.expression();
13002 if (this.consume(':')) {
13003 consequent = this.expression();
13004 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13010 logicalOR: function() {
13011 var left = this.logicalAND();
13012 while (this.expect('||')) {
13013 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13018 logicalAND: function() {
13019 var left = this.equality();
13020 while (this.expect('&&')) {
13021 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13026 equality: function() {
13027 var left = this.relational();
13029 while ((token = this.expect('==','!=','===','!=='))) {
13030 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13035 relational: function() {
13036 var left = this.additive();
13038 while ((token = this.expect('<', '>', '<=', '>='))) {
13039 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13044 additive: function() {
13045 var left = this.multiplicative();
13047 while ((token = this.expect('+','-'))) {
13048 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13053 multiplicative: function() {
13054 var left = this.unary();
13056 while ((token = this.expect('*','/','%'))) {
13057 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13062 unary: function() {
13064 if ((token = this.expect('+', '-', '!'))) {
13065 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13067 return this.primary();
13071 primary: function() {
13073 if (this.expect('(')) {
13074 primary = this.filterChain();
13076 } else if (this.expect('[')) {
13077 primary = this.arrayDeclaration();
13078 } else if (this.expect('{')) {
13079 primary = this.object();
13080 } else if (this.constants.hasOwnProperty(this.peek().text)) {
13081 primary = copy(this.constants[this.consume().text]);
13082 } else if (this.peek().identifier) {
13083 primary = this.identifier();
13084 } else if (this.peek().constant) {
13085 primary = this.constant();
13087 this.throwError('not a primary expression', this.peek());
13091 while ((next = this.expect('(', '[', '.'))) {
13092 if (next.text === '(') {
13093 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13095 } else if (next.text === '[') {
13096 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13098 } else if (next.text === '.') {
13099 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13101 this.throwError('IMPOSSIBLE');
13107 filter: function(baseExpression) {
13108 var args = [baseExpression];
13109 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13111 while (this.expect(':')) {
13112 args.push(this.expression());
13118 parseArguments: function() {
13120 if (this.peekToken().text !== ')') {
13122 args.push(this.expression());
13123 } while (this.expect(','));
13128 identifier: function() {
13129 var token = this.consume();
13130 if (!token.identifier) {
13131 this.throwError('is not a valid identifier', token);
13133 return { type: AST.Identifier, name: token.text };
13136 constant: function() {
13137 // TODO check that it is a constant
13138 return { type: AST.Literal, value: this.consume().value };
13141 arrayDeclaration: function() {
13143 if (this.peekToken().text !== ']') {
13145 if (this.peek(']')) {
13146 // Support trailing commas per ES5.1.
13149 elements.push(this.expression());
13150 } while (this.expect(','));
13154 return { type: AST.ArrayExpression, elements: elements };
13157 object: function() {
13158 var properties = [], property;
13159 if (this.peekToken().text !== '}') {
13161 if (this.peek('}')) {
13162 // Support trailing commas per ES5.1.
13165 property = {type: AST.Property, kind: 'init'};
13166 if (this.peek().constant) {
13167 property.key = this.constant();
13168 } else if (this.peek().identifier) {
13169 property.key = this.identifier();
13171 this.throwError("invalid key", this.peek());
13174 property.value = this.expression();
13175 properties.push(property);
13176 } while (this.expect(','));
13180 return {type: AST.ObjectExpression, properties: properties };
13183 throwError: function(msg, token) {
13184 throw $parseMinErr('syntax',
13185 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13186 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13189 consume: function(e1) {
13190 if (this.tokens.length === 0) {
13191 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13194 var token = this.expect(e1);
13196 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13201 peekToken: function() {
13202 if (this.tokens.length === 0) {
13203 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13205 return this.tokens[0];
13208 peek: function(e1, e2, e3, e4) {
13209 return this.peekAhead(0, e1, e2, e3, e4);
13212 peekAhead: function(i, e1, e2, e3, e4) {
13213 if (this.tokens.length > i) {
13214 var token = this.tokens[i];
13215 var t = token.text;
13216 if (t === e1 || t === e2 || t === e3 || t === e4 ||
13217 (!e1 && !e2 && !e3 && !e4)) {
13224 expect: function(e1, e2, e3, e4) {
13225 var token = this.peek(e1, e2, e3, e4);
13227 this.tokens.shift();
13234 /* `undefined` is not a constant, it is an identifier,
13235 * but using it as an identifier is not supported
13238 'true': { type: AST.Literal, value: true },
13239 'false': { type: AST.Literal, value: false },
13240 'null': { type: AST.Literal, value: null },
13241 'undefined': {type: AST.Literal, value: undefined },
13242 'this': {type: AST.ThisExpression }
13246 function ifDefined(v, d) {
13247 return typeof v !== 'undefined' ? v : d;
13250 function plusFn(l, r) {
13251 if (typeof l === 'undefined') return r;
13252 if (typeof r === 'undefined') return l;
13256 function isStateless($filter, filterName) {
13257 var fn = $filter(filterName);
13258 return !fn.$stateful;
13261 function findConstantAndWatchExpressions(ast, $filter) {
13264 switch (ast.type) {
13266 allConstants = true;
13267 forEach(ast.body, function(expr) {
13268 findConstantAndWatchExpressions(expr.expression, $filter);
13269 allConstants = allConstants && expr.expression.constant;
13271 ast.constant = allConstants;
13274 ast.constant = true;
13277 case AST.UnaryExpression:
13278 findConstantAndWatchExpressions(ast.argument, $filter);
13279 ast.constant = ast.argument.constant;
13280 ast.toWatch = ast.argument.toWatch;
13282 case AST.BinaryExpression:
13283 findConstantAndWatchExpressions(ast.left, $filter);
13284 findConstantAndWatchExpressions(ast.right, $filter);
13285 ast.constant = ast.left.constant && ast.right.constant;
13286 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13288 case AST.LogicalExpression:
13289 findConstantAndWatchExpressions(ast.left, $filter);
13290 findConstantAndWatchExpressions(ast.right, $filter);
13291 ast.constant = ast.left.constant && ast.right.constant;
13292 ast.toWatch = ast.constant ? [] : [ast];
13294 case AST.ConditionalExpression:
13295 findConstantAndWatchExpressions(ast.test, $filter);
13296 findConstantAndWatchExpressions(ast.alternate, $filter);
13297 findConstantAndWatchExpressions(ast.consequent, $filter);
13298 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13299 ast.toWatch = ast.constant ? [] : [ast];
13301 case AST.Identifier:
13302 ast.constant = false;
13303 ast.toWatch = [ast];
13305 case AST.MemberExpression:
13306 findConstantAndWatchExpressions(ast.object, $filter);
13307 if (ast.computed) {
13308 findConstantAndWatchExpressions(ast.property, $filter);
13310 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13311 ast.toWatch = [ast];
13313 case AST.CallExpression:
13314 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13316 forEach(ast.arguments, function(expr) {
13317 findConstantAndWatchExpressions(expr, $filter);
13318 allConstants = allConstants && expr.constant;
13319 if (!expr.constant) {
13320 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13323 ast.constant = allConstants;
13324 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13326 case AST.AssignmentExpression:
13327 findConstantAndWatchExpressions(ast.left, $filter);
13328 findConstantAndWatchExpressions(ast.right, $filter);
13329 ast.constant = ast.left.constant && ast.right.constant;
13330 ast.toWatch = [ast];
13332 case AST.ArrayExpression:
13333 allConstants = true;
13335 forEach(ast.elements, function(expr) {
13336 findConstantAndWatchExpressions(expr, $filter);
13337 allConstants = allConstants && expr.constant;
13338 if (!expr.constant) {
13339 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13342 ast.constant = allConstants;
13343 ast.toWatch = argsToWatch;
13345 case AST.ObjectExpression:
13346 allConstants = true;
13348 forEach(ast.properties, function(property) {
13349 findConstantAndWatchExpressions(property.value, $filter);
13350 allConstants = allConstants && property.value.constant;
13351 if (!property.value.constant) {
13352 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13355 ast.constant = allConstants;
13356 ast.toWatch = argsToWatch;
13358 case AST.ThisExpression:
13359 ast.constant = false;
13365 function getInputs(body) {
13366 if (body.length != 1) return;
13367 var lastExpression = body[0].expression;
13368 var candidate = lastExpression.toWatch;
13369 if (candidate.length !== 1) return candidate;
13370 return candidate[0] !== lastExpression ? candidate : undefined;
13373 function isAssignable(ast) {
13374 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13377 function assignableAST(ast) {
13378 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13379 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13383 function isLiteral(ast) {
13384 return ast.body.length === 0 ||
13385 ast.body.length === 1 && (
13386 ast.body[0].expression.type === AST.Literal ||
13387 ast.body[0].expression.type === AST.ArrayExpression ||
13388 ast.body[0].expression.type === AST.ObjectExpression);
13391 function isConstant(ast) {
13392 return ast.constant;
13395 function ASTCompiler(astBuilder, $filter) {
13396 this.astBuilder = astBuilder;
13397 this.$filter = $filter;
13400 ASTCompiler.prototype = {
13401 compile: function(expression, expensiveChecks) {
13403 var ast = this.astBuilder.ast(expression);
13407 expensiveChecks: expensiveChecks,
13408 fn: {vars: [], body: [], own: {}},
13409 assign: {vars: [], body: [], own: {}},
13412 findConstantAndWatchExpressions(ast, self.$filter);
13415 this.stage = 'assign';
13416 if ((assignable = assignableAST(ast))) {
13417 this.state.computing = 'assign';
13418 var result = this.nextId();
13419 this.recurse(assignable, result);
13420 this.return_(result);
13421 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13423 var toWatch = getInputs(ast.body);
13424 self.stage = 'inputs';
13425 forEach(toWatch, function(watch, key) {
13426 var fnKey = 'fn' + key;
13427 self.state[fnKey] = {vars: [], body: [], own: {}};
13428 self.state.computing = fnKey;
13429 var intoId = self.nextId();
13430 self.recurse(watch, intoId);
13431 self.return_(intoId);
13432 self.state.inputs.push(fnKey);
13433 watch.watchId = key;
13435 this.state.computing = 'fn';
13436 this.stage = 'main';
13439 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13440 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13441 '"' + this.USE + ' ' + this.STRICT + '";\n' +
13442 this.filterPrefix() +
13443 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13449 var fn = (new Function('$filter',
13450 'ensureSafeMemberName',
13451 'ensureSafeObject',
13452 'ensureSafeFunction',
13454 'ensureSafeAssignContext',
13460 ensureSafeMemberName,
13462 ensureSafeFunction,
13464 ensureSafeAssignContext,
13469 this.state = this.stage = undefined;
13470 fn.literal = isLiteral(ast);
13471 fn.constant = isConstant(ast);
13479 watchFns: function() {
13481 var fns = this.state.inputs;
13483 forEach(fns, function(name) {
13484 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13487 result.push('fn.inputs=[' + fns.join(',') + '];');
13489 return result.join('');
13492 generateFunction: function(name, params) {
13493 return 'function(' + params + '){' +
13494 this.varsPrefix(name) +
13499 filterPrefix: function() {
13502 forEach(this.state.filters, function(id, filter) {
13503 parts.push(id + '=$filter(' + self.escape(filter) + ')');
13505 if (parts.length) return 'var ' + parts.join(',') + ';';
13509 varsPrefix: function(section) {
13510 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13513 body: function(section) {
13514 return this.state[section].body.join('');
13517 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13518 var left, right, self = this, args, expression;
13519 recursionFn = recursionFn || noop;
13520 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13521 intoId = intoId || this.nextId();
13523 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13524 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13528 switch (ast.type) {
13530 forEach(ast.body, function(expression, pos) {
13531 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13532 if (pos !== ast.body.length - 1) {
13533 self.current().body.push(right, ';');
13535 self.return_(right);
13540 expression = this.escape(ast.value);
13541 this.assign(intoId, expression);
13542 recursionFn(expression);
13544 case AST.UnaryExpression:
13545 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13546 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13547 this.assign(intoId, expression);
13548 recursionFn(expression);
13550 case AST.BinaryExpression:
13551 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13552 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13553 if (ast.operator === '+') {
13554 expression = this.plus(left, right);
13555 } else if (ast.operator === '-') {
13556 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13558 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13560 this.assign(intoId, expression);
13561 recursionFn(expression);
13563 case AST.LogicalExpression:
13564 intoId = intoId || this.nextId();
13565 self.recurse(ast.left, intoId);
13566 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13567 recursionFn(intoId);
13569 case AST.ConditionalExpression:
13570 intoId = intoId || this.nextId();
13571 self.recurse(ast.test, intoId);
13572 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13573 recursionFn(intoId);
13575 case AST.Identifier:
13576 intoId = intoId || this.nextId();
13578 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13579 nameId.computed = false;
13580 nameId.name = ast.name;
13582 ensureSafeMemberName(ast.name);
13583 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13585 self.if_(self.stage === 'inputs' || 's', function() {
13586 if (create && create !== 1) {
13588 self.not(self.nonComputedMember('s', ast.name)),
13589 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13591 self.assign(intoId, self.nonComputedMember('s', ast.name));
13593 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13595 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13596 self.addEnsureSafeObject(intoId);
13598 recursionFn(intoId);
13600 case AST.MemberExpression:
13601 left = nameId && (nameId.context = this.nextId()) || this.nextId();
13602 intoId = intoId || this.nextId();
13603 self.recurse(ast.object, left, undefined, function() {
13604 self.if_(self.notNull(left), function() {
13605 if (ast.computed) {
13606 right = self.nextId();
13607 self.recurse(ast.property, right);
13608 self.getStringValue(right);
13609 self.addEnsureSafeMemberName(right);
13610 if (create && create !== 1) {
13611 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13613 expression = self.ensureSafeObject(self.computedMember(left, right));
13614 self.assign(intoId, expression);
13616 nameId.computed = true;
13617 nameId.name = right;
13620 ensureSafeMemberName(ast.property.name);
13621 if (create && create !== 1) {
13622 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13624 expression = self.nonComputedMember(left, ast.property.name);
13625 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13626 expression = self.ensureSafeObject(expression);
13628 self.assign(intoId, expression);
13630 nameId.computed = false;
13631 nameId.name = ast.property.name;
13635 self.assign(intoId, 'undefined');
13637 recursionFn(intoId);
13640 case AST.CallExpression:
13641 intoId = intoId || this.nextId();
13643 right = self.filter(ast.callee.name);
13645 forEach(ast.arguments, function(expr) {
13646 var argument = self.nextId();
13647 self.recurse(expr, argument);
13648 args.push(argument);
13650 expression = right + '(' + args.join(',') + ')';
13651 self.assign(intoId, expression);
13652 recursionFn(intoId);
13654 right = self.nextId();
13657 self.recurse(ast.callee, right, left, function() {
13658 self.if_(self.notNull(right), function() {
13659 self.addEnsureSafeFunction(right);
13660 forEach(ast.arguments, function(expr) {
13661 self.recurse(expr, self.nextId(), undefined, function(argument) {
13662 args.push(self.ensureSafeObject(argument));
13666 if (!self.state.expensiveChecks) {
13667 self.addEnsureSafeObject(left.context);
13669 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13671 expression = right + '(' + args.join(',') + ')';
13673 expression = self.ensureSafeObject(expression);
13674 self.assign(intoId, expression);
13676 self.assign(intoId, 'undefined');
13678 recursionFn(intoId);
13682 case AST.AssignmentExpression:
13683 right = this.nextId();
13685 if (!isAssignable(ast.left)) {
13686 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13688 this.recurse(ast.left, undefined, left, function() {
13689 self.if_(self.notNull(left.context), function() {
13690 self.recurse(ast.right, right);
13691 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13692 self.addEnsureSafeAssignContext(left.context);
13693 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13694 self.assign(intoId, expression);
13695 recursionFn(intoId || expression);
13699 case AST.ArrayExpression:
13701 forEach(ast.elements, function(expr) {
13702 self.recurse(expr, self.nextId(), undefined, function(argument) {
13703 args.push(argument);
13706 expression = '[' + args.join(',') + ']';
13707 this.assign(intoId, expression);
13708 recursionFn(expression);
13710 case AST.ObjectExpression:
13712 forEach(ast.properties, function(property) {
13713 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13714 args.push(self.escape(
13715 property.key.type === AST.Identifier ? property.key.name :
13716 ('' + property.key.value)) +
13720 expression = '{' + args.join(',') + '}';
13721 this.assign(intoId, expression);
13722 recursionFn(expression);
13724 case AST.ThisExpression:
13725 this.assign(intoId, 's');
13728 case AST.NGValueParameter:
13729 this.assign(intoId, 'v');
13735 getHasOwnProperty: function(element, property) {
13736 var key = element + '.' + property;
13737 var own = this.current().own;
13738 if (!own.hasOwnProperty(key)) {
13739 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13744 assign: function(id, value) {
13746 this.current().body.push(id, '=', value, ';');
13750 filter: function(filterName) {
13751 if (!this.state.filters.hasOwnProperty(filterName)) {
13752 this.state.filters[filterName] = this.nextId(true);
13754 return this.state.filters[filterName];
13757 ifDefined: function(id, defaultValue) {
13758 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13761 plus: function(left, right) {
13762 return 'plus(' + left + ',' + right + ')';
13765 return_: function(id) {
13766 this.current().body.push('return ', id, ';');
13769 if_: function(test, alternate, consequent) {
13770 if (test === true) {
13773 var body = this.current().body;
13774 body.push('if(', test, '){');
13778 body.push('else{');
13785 not: function(expression) {
13786 return '!(' + expression + ')';
13789 notNull: function(expression) {
13790 return expression + '!=null';
13793 nonComputedMember: function(left, right) {
13794 return left + '.' + right;
13797 computedMember: function(left, right) {
13798 return left + '[' + right + ']';
13801 member: function(left, right, computed) {
13802 if (computed) return this.computedMember(left, right);
13803 return this.nonComputedMember(left, right);
13806 addEnsureSafeObject: function(item) {
13807 this.current().body.push(this.ensureSafeObject(item), ';');
13810 addEnsureSafeMemberName: function(item) {
13811 this.current().body.push(this.ensureSafeMemberName(item), ';');
13814 addEnsureSafeFunction: function(item) {
13815 this.current().body.push(this.ensureSafeFunction(item), ';');
13818 addEnsureSafeAssignContext: function(item) {
13819 this.current().body.push(this.ensureSafeAssignContext(item), ';');
13822 ensureSafeObject: function(item) {
13823 return 'ensureSafeObject(' + item + ',text)';
13826 ensureSafeMemberName: function(item) {
13827 return 'ensureSafeMemberName(' + item + ',text)';
13830 ensureSafeFunction: function(item) {
13831 return 'ensureSafeFunction(' + item + ',text)';
13834 getStringValue: function(item) {
13835 this.assign(item, 'getStringValue(' + item + ',text)');
13838 ensureSafeAssignContext: function(item) {
13839 return 'ensureSafeAssignContext(' + item + ',text)';
13842 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13844 return function() {
13845 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13849 lazyAssign: function(id, value) {
13851 return function() {
13852 self.assign(id, value);
13856 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13858 stringEscapeFn: function(c) {
13859 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13862 escape: function(value) {
13863 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13864 if (isNumber(value)) return value.toString();
13865 if (value === true) return 'true';
13866 if (value === false) return 'false';
13867 if (value === null) return 'null';
13868 if (typeof value === 'undefined') return 'undefined';
13870 throw $parseMinErr('esc', 'IMPOSSIBLE');
13873 nextId: function(skip, init) {
13874 var id = 'v' + (this.state.nextId++);
13876 this.current().vars.push(id + (init ? '=' + init : ''));
13881 current: function() {
13882 return this.state[this.state.computing];
13887 function ASTInterpreter(astBuilder, $filter) {
13888 this.astBuilder = astBuilder;
13889 this.$filter = $filter;
13892 ASTInterpreter.prototype = {
13893 compile: function(expression, expensiveChecks) {
13895 var ast = this.astBuilder.ast(expression);
13896 this.expression = expression;
13897 this.expensiveChecks = expensiveChecks;
13898 findConstantAndWatchExpressions(ast, self.$filter);
13901 if ((assignable = assignableAST(ast))) {
13902 assign = this.recurse(assignable);
13904 var toWatch = getInputs(ast.body);
13908 forEach(toWatch, function(watch, key) {
13909 var input = self.recurse(watch);
13910 watch.input = input;
13911 inputs.push(input);
13912 watch.watchId = key;
13915 var expressions = [];
13916 forEach(ast.body, function(expression) {
13917 expressions.push(self.recurse(expression.expression));
13919 var fn = ast.body.length === 0 ? function() {} :
13920 ast.body.length === 1 ? expressions[0] :
13921 function(scope, locals) {
13923 forEach(expressions, function(exp) {
13924 lastValue = exp(scope, locals);
13929 fn.assign = function(scope, value, locals) {
13930 return assign(scope, locals, value);
13934 fn.inputs = inputs;
13936 fn.literal = isLiteral(ast);
13937 fn.constant = isConstant(ast);
13941 recurse: function(ast, context, create) {
13942 var left, right, self = this, args, expression;
13944 return this.inputs(ast.input, ast.watchId);
13946 switch (ast.type) {
13948 return this.value(ast.value, context);
13949 case AST.UnaryExpression:
13950 right = this.recurse(ast.argument);
13951 return this['unary' + ast.operator](right, context);
13952 case AST.BinaryExpression:
13953 left = this.recurse(ast.left);
13954 right = this.recurse(ast.right);
13955 return this['binary' + ast.operator](left, right, context);
13956 case AST.LogicalExpression:
13957 left = this.recurse(ast.left);
13958 right = this.recurse(ast.right);
13959 return this['binary' + ast.operator](left, right, context);
13960 case AST.ConditionalExpression:
13961 return this['ternary?:'](
13962 this.recurse(ast.test),
13963 this.recurse(ast.alternate),
13964 this.recurse(ast.consequent),
13967 case AST.Identifier:
13968 ensureSafeMemberName(ast.name, self.expression);
13969 return self.identifier(ast.name,
13970 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13971 context, create, self.expression);
13972 case AST.MemberExpression:
13973 left = this.recurse(ast.object, false, !!create);
13974 if (!ast.computed) {
13975 ensureSafeMemberName(ast.property.name, self.expression);
13976 right = ast.property.name;
13978 if (ast.computed) right = this.recurse(ast.property);
13979 return ast.computed ?
13980 this.computedMember(left, right, context, create, self.expression) :
13981 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13982 case AST.CallExpression:
13984 forEach(ast.arguments, function(expr) {
13985 args.push(self.recurse(expr));
13987 if (ast.filter) right = this.$filter(ast.callee.name);
13988 if (!ast.filter) right = this.recurse(ast.callee, true);
13989 return ast.filter ?
13990 function(scope, locals, assign, inputs) {
13992 for (var i = 0; i < args.length; ++i) {
13993 values.push(args[i](scope, locals, assign, inputs));
13995 var value = right.apply(undefined, values, inputs);
13996 return context ? {context: undefined, name: undefined, value: value} : value;
13998 function(scope, locals, assign, inputs) {
13999 var rhs = right(scope, locals, assign, inputs);
14001 if (rhs.value != null) {
14002 ensureSafeObject(rhs.context, self.expression);
14003 ensureSafeFunction(rhs.value, self.expression);
14005 for (var i = 0; i < args.length; ++i) {
14006 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14008 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14010 return context ? {value: value} : value;
14012 case AST.AssignmentExpression:
14013 left = this.recurse(ast.left, true, 1);
14014 right = this.recurse(ast.right);
14015 return function(scope, locals, assign, inputs) {
14016 var lhs = left(scope, locals, assign, inputs);
14017 var rhs = right(scope, locals, assign, inputs);
14018 ensureSafeObject(lhs.value, self.expression);
14019 ensureSafeAssignContext(lhs.context);
14020 lhs.context[lhs.name] = rhs;
14021 return context ? {value: rhs} : rhs;
14023 case AST.ArrayExpression:
14025 forEach(ast.elements, function(expr) {
14026 args.push(self.recurse(expr));
14028 return function(scope, locals, assign, inputs) {
14030 for (var i = 0; i < args.length; ++i) {
14031 value.push(args[i](scope, locals, assign, inputs));
14033 return context ? {value: value} : value;
14035 case AST.ObjectExpression:
14037 forEach(ast.properties, function(property) {
14038 args.push({key: property.key.type === AST.Identifier ?
14039 property.key.name :
14040 ('' + property.key.value),
14041 value: self.recurse(property.value)
14044 return function(scope, locals, assign, inputs) {
14046 for (var i = 0; i < args.length; ++i) {
14047 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14049 return context ? {value: value} : value;
14051 case AST.ThisExpression:
14052 return function(scope) {
14053 return context ? {value: scope} : scope;
14055 case AST.NGValueParameter:
14056 return function(scope, locals, assign, inputs) {
14057 return context ? {value: assign} : assign;
14062 'unary+': function(argument, context) {
14063 return function(scope, locals, assign, inputs) {
14064 var arg = argument(scope, locals, assign, inputs);
14065 if (isDefined(arg)) {
14070 return context ? {value: arg} : arg;
14073 'unary-': function(argument, context) {
14074 return function(scope, locals, assign, inputs) {
14075 var arg = argument(scope, locals, assign, inputs);
14076 if (isDefined(arg)) {
14081 return context ? {value: arg} : arg;
14084 'unary!': function(argument, context) {
14085 return function(scope, locals, assign, inputs) {
14086 var arg = !argument(scope, locals, assign, inputs);
14087 return context ? {value: arg} : arg;
14090 'binary+': function(left, right, context) {
14091 return function(scope, locals, assign, inputs) {
14092 var lhs = left(scope, locals, assign, inputs);
14093 var rhs = right(scope, locals, assign, inputs);
14094 var arg = plusFn(lhs, rhs);
14095 return context ? {value: arg} : arg;
14098 'binary-': function(left, right, context) {
14099 return function(scope, locals, assign, inputs) {
14100 var lhs = left(scope, locals, assign, inputs);
14101 var rhs = right(scope, locals, assign, inputs);
14102 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14103 return context ? {value: arg} : arg;
14106 'binary*': function(left, right, context) {
14107 return function(scope, locals, assign, inputs) {
14108 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14109 return context ? {value: arg} : arg;
14112 'binary/': function(left, right, context) {
14113 return function(scope, locals, assign, inputs) {
14114 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14115 return context ? {value: arg} : arg;
14118 'binary%': function(left, right, context) {
14119 return function(scope, locals, assign, inputs) {
14120 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14121 return context ? {value: arg} : arg;
14124 'binary===': function(left, right, context) {
14125 return function(scope, locals, assign, inputs) {
14126 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14127 return context ? {value: arg} : arg;
14130 'binary!==': function(left, right, context) {
14131 return function(scope, locals, assign, inputs) {
14132 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14133 return context ? {value: arg} : arg;
14136 'binary==': function(left, right, context) {
14137 return function(scope, locals, assign, inputs) {
14138 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14139 return context ? {value: arg} : arg;
14142 'binary!=': function(left, right, context) {
14143 return function(scope, locals, assign, inputs) {
14144 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14145 return context ? {value: arg} : arg;
14148 'binary<': function(left, right, context) {
14149 return function(scope, locals, assign, inputs) {
14150 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14151 return context ? {value: arg} : arg;
14154 'binary>': function(left, right, context) {
14155 return function(scope, locals, assign, inputs) {
14156 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14157 return context ? {value: arg} : arg;
14160 'binary<=': function(left, right, context) {
14161 return function(scope, locals, assign, inputs) {
14162 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14163 return context ? {value: arg} : arg;
14166 'binary>=': function(left, right, context) {
14167 return function(scope, locals, assign, inputs) {
14168 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14169 return context ? {value: arg} : arg;
14172 'binary&&': function(left, right, context) {
14173 return function(scope, locals, assign, inputs) {
14174 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14175 return context ? {value: arg} : arg;
14178 'binary||': function(left, right, context) {
14179 return function(scope, locals, assign, inputs) {
14180 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14181 return context ? {value: arg} : arg;
14184 'ternary?:': function(test, alternate, consequent, context) {
14185 return function(scope, locals, assign, inputs) {
14186 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14187 return context ? {value: arg} : arg;
14190 value: function(value, context) {
14191 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14193 identifier: function(name, expensiveChecks, context, create, expression) {
14194 return function(scope, locals, assign, inputs) {
14195 var base = locals && (name in locals) ? locals : scope;
14196 if (create && create !== 1 && base && !(base[name])) {
14199 var value = base ? base[name] : undefined;
14200 if (expensiveChecks) {
14201 ensureSafeObject(value, expression);
14204 return {context: base, name: name, value: value};
14210 computedMember: function(left, right, context, create, expression) {
14211 return function(scope, locals, assign, inputs) {
14212 var lhs = left(scope, locals, assign, inputs);
14216 rhs = right(scope, locals, assign, inputs);
14217 rhs = getStringValue(rhs);
14218 ensureSafeMemberName(rhs, expression);
14219 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14223 ensureSafeObject(value, expression);
14226 return {context: lhs, name: rhs, value: value};
14232 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14233 return function(scope, locals, assign, inputs) {
14234 var lhs = left(scope, locals, assign, inputs);
14235 if (create && create !== 1 && lhs && !(lhs[right])) {
14238 var value = lhs != null ? lhs[right] : undefined;
14239 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14240 ensureSafeObject(value, expression);
14243 return {context: lhs, name: right, value: value};
14249 inputs: function(input, watchId) {
14250 return function(scope, value, locals, inputs) {
14251 if (inputs) return inputs[watchId];
14252 return input(scope, value, locals);
14260 var Parser = function(lexer, $filter, options) {
14261 this.lexer = lexer;
14262 this.$filter = $filter;
14263 this.options = options;
14264 this.ast = new AST(this.lexer);
14265 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14266 new ASTCompiler(this.ast, $filter);
14269 Parser.prototype = {
14270 constructor: Parser,
14272 parse: function(text) {
14273 return this.astCompiler.compile(text, this.options.expensiveChecks);
14277 var getterFnCacheDefault = createMap();
14278 var getterFnCacheExpensive = createMap();
14280 function isPossiblyDangerousMemberName(name) {
14281 return name == 'constructor';
14284 var objectValueOf = Object.prototype.valueOf;
14286 function getValueOf(value) {
14287 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14290 ///////////////////////////////////
14299 * Converts Angular {@link guide/expression expression} into a function.
14302 * var getter = $parse('user.name');
14303 * var setter = getter.assign;
14304 * var context = {user:{name:'angular'}};
14305 * var locals = {user:{name:'local'}};
14307 * expect(getter(context)).toEqual('angular');
14308 * setter(context, 'newValue');
14309 * expect(context.user.name).toEqual('newValue');
14310 * expect(getter(context, locals)).toEqual('local');
14314 * @param {string} expression String expression to compile.
14315 * @returns {function(context, locals)} a function which represents the compiled expression:
14317 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14318 * are evaluated against (typically a scope object).
14319 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14322 * The returned function also has the following properties:
14323 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14325 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14326 * constant literals.
14327 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14328 * set to a function to change its value on the given context.
14335 * @name $parseProvider
14338 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14341 function $ParseProvider() {
14342 var cacheDefault = createMap();
14343 var cacheExpensive = createMap();
14345 this.$get = ['$filter', function($filter) {
14346 var noUnsafeEval = csp().noUnsafeEval;
14347 var $parseOptions = {
14349 expensiveChecks: false
14351 $parseOptionsExpensive = {
14353 expensiveChecks: true
14356 return function $parse(exp, interceptorFn, expensiveChecks) {
14357 var parsedExpression, oneTime, cacheKey;
14359 switch (typeof exp) {
14364 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14365 parsedExpression = cache[cacheKey];
14367 if (!parsedExpression) {
14368 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14370 exp = exp.substring(2);
14372 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14373 var lexer = new Lexer(parseOptions);
14374 var parser = new Parser(lexer, $filter, parseOptions);
14375 parsedExpression = parser.parse(exp);
14376 if (parsedExpression.constant) {
14377 parsedExpression.$$watchDelegate = constantWatchDelegate;
14378 } else if (oneTime) {
14379 parsedExpression.$$watchDelegate = parsedExpression.literal ?
14380 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14381 } else if (parsedExpression.inputs) {
14382 parsedExpression.$$watchDelegate = inputsWatchDelegate;
14384 cache[cacheKey] = parsedExpression;
14386 return addInterceptor(parsedExpression, interceptorFn);
14389 return addInterceptor(exp, interceptorFn);
14396 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14398 if (newValue == null || oldValueOfValue == null) { // null/undefined
14399 return newValue === oldValueOfValue;
14402 if (typeof newValue === 'object') {
14404 // attempt to convert the value to a primitive type
14405 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14406 // be cheaply dirty-checked
14407 newValue = getValueOf(newValue);
14409 if (typeof newValue === 'object') {
14410 // objects/arrays are not supported - deep-watching them would be too expensive
14414 // fall-through to the primitive equality check
14418 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14421 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14422 var inputExpressions = parsedExpression.inputs;
14425 if (inputExpressions.length === 1) {
14426 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14427 inputExpressions = inputExpressions[0];
14428 return scope.$watch(function expressionInputWatch(scope) {
14429 var newInputValue = inputExpressions(scope);
14430 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14431 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14432 oldInputValueOf = newInputValue && getValueOf(newInputValue);
14435 }, listener, objectEquality, prettyPrintExpression);
14438 var oldInputValueOfValues = [];
14439 var oldInputValues = [];
14440 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14441 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14442 oldInputValues[i] = null;
14445 return scope.$watch(function expressionInputsWatch(scope) {
14446 var changed = false;
14448 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14449 var newInputValue = inputExpressions[i](scope);
14450 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14451 oldInputValues[i] = newInputValue;
14452 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14457 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14461 }, listener, objectEquality, prettyPrintExpression);
14464 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14465 var unwatch, lastValue;
14466 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14467 return parsedExpression(scope);
14468 }, function oneTimeListener(value, old, scope) {
14470 if (isFunction(listener)) {
14471 listener.apply(this, arguments);
14473 if (isDefined(value)) {
14474 scope.$$postDigest(function() {
14475 if (isDefined(lastValue)) {
14480 }, objectEquality);
14483 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14484 var unwatch, lastValue;
14485 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14486 return parsedExpression(scope);
14487 }, function oneTimeListener(value, old, scope) {
14489 if (isFunction(listener)) {
14490 listener.call(this, value, old, scope);
14492 if (isAllDefined(value)) {
14493 scope.$$postDigest(function() {
14494 if (isAllDefined(lastValue)) unwatch();
14497 }, objectEquality);
14499 function isAllDefined(value) {
14500 var allDefined = true;
14501 forEach(value, function(val) {
14502 if (!isDefined(val)) allDefined = false;
14508 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14510 return unwatch = scope.$watch(function constantWatch(scope) {
14511 return parsedExpression(scope);
14512 }, function constantListener(value, old, scope) {
14513 if (isFunction(listener)) {
14514 listener.apply(this, arguments);
14517 }, objectEquality);
14520 function addInterceptor(parsedExpression, interceptorFn) {
14521 if (!interceptorFn) return parsedExpression;
14522 var watchDelegate = parsedExpression.$$watchDelegate;
14523 var useInputs = false;
14526 watchDelegate !== oneTimeLiteralWatchDelegate &&
14527 watchDelegate !== oneTimeWatchDelegate;
14529 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14530 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14531 return interceptorFn(value, scope, locals);
14532 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14533 var value = parsedExpression(scope, locals, assign, inputs);
14534 var result = interceptorFn(value, scope, locals);
14535 // we only return the interceptor's result if the
14536 // initial value is defined (for bind-once)
14537 return isDefined(value) ? result : value;
14540 // Propagate $$watchDelegates other then inputsWatchDelegate
14541 if (parsedExpression.$$watchDelegate &&
14542 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14543 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14544 } else if (!interceptorFn.$stateful) {
14545 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14546 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14547 fn.$$watchDelegate = inputsWatchDelegate;
14548 useInputs = !parsedExpression.inputs;
14549 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14560 * @requires $rootScope
14563 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14564 * when they are done processing.
14566 * This is an implementation of promises/deferred objects inspired by
14567 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14569 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14570 * implementations, and the other which resembles ES6 promises to some degree.
14574 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14575 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14576 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14578 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14581 * It can be used like so:
14584 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14585 * // are available in the current lexical scope (they could have been injected or passed in).
14587 * function asyncGreet(name) {
14588 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
14589 * return $q(function(resolve, reject) {
14590 * setTimeout(function() {
14591 * if (okToGreet(name)) {
14592 * resolve('Hello, ' + name + '!');
14594 * reject('Greeting ' + name + ' is not allowed.');
14600 * var promise = asyncGreet('Robin Hood');
14601 * promise.then(function(greeting) {
14602 * alert('Success: ' + greeting);
14603 * }, function(reason) {
14604 * alert('Failed: ' + reason);
14608 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14610 * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14612 * However, the more traditional CommonJS-style usage is still available, and documented below.
14614 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14615 * interface for interacting with an object that represents the result of an action that is
14616 * performed asynchronously, and may or may not be finished at any given point in time.
14618 * From the perspective of dealing with error handling, deferred and promise APIs are to
14619 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14622 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14623 * // are available in the current lexical scope (they could have been injected or passed in).
14625 * function asyncGreet(name) {
14626 * var deferred = $q.defer();
14628 * setTimeout(function() {
14629 * deferred.notify('About to greet ' + name + '.');
14631 * if (okToGreet(name)) {
14632 * deferred.resolve('Hello, ' + name + '!');
14634 * deferred.reject('Greeting ' + name + ' is not allowed.');
14638 * return deferred.promise;
14641 * var promise = asyncGreet('Robin Hood');
14642 * promise.then(function(greeting) {
14643 * alert('Success: ' + greeting);
14644 * }, function(reason) {
14645 * alert('Failed: ' + reason);
14646 * }, function(update) {
14647 * alert('Got notification: ' + update);
14651 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14652 * comes in the way of guarantees that promise and deferred APIs make, see
14653 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14655 * Additionally the promise api allows for composition that is very hard to do with the
14656 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14657 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14658 * section on serial or parallel joining of promises.
14660 * # The Deferred API
14662 * A new instance of deferred is constructed by calling `$q.defer()`.
14664 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14665 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14670 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14671 * constructed via `$q.reject`, the promise will be rejected instead.
14672 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14673 * resolving it with a rejection constructed via `$q.reject`.
14674 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14675 * multiple times before the promise is either resolved or rejected.
14679 * - promise – `{Promise}` – promise object associated with this deferred.
14682 * # The Promise API
14684 * A new promise instance is created when a deferred instance is created and can be retrieved by
14685 * calling `deferred.promise`.
14687 * The purpose of the promise object is to allow for interested parties to get access to the result
14688 * of the deferred task when it completes.
14692 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14693 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14694 * as soon as the result is available. The callbacks are called with a single argument: the result
14695 * or rejection reason. Additionally, the notify callback may be called zero or more times to
14696 * provide a progress indication, before the promise is resolved or rejected.
14698 * This method *returns a new promise* which is resolved or rejected via the return value of the
14699 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14700 * with the value which is resolved in that promise using
14701 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14702 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14703 * resolved or rejected from the notifyCallback method.
14705 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14707 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14708 * but to do so without modifying the final value. This is useful to release resources or do some
14709 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14710 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14711 * more information.
14713 * # Chaining promises
14715 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14716 * possible to create a chain of promises:
14719 * promiseB = promiseA.then(function(result) {
14720 * return result + 1;
14723 * // promiseB will be resolved immediately after promiseA is resolved and its value
14724 * // will be the result of promiseA incremented by 1
14727 * It is possible to create chains of any length and since a promise can be resolved with another
14728 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14729 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14730 * $http's response interceptors.
14733 * # Differences between Kris Kowal's Q and $q
14735 * There are two main differences:
14737 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14738 * mechanism in angular, which means faster propagation of resolution or rejection into your
14739 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
14740 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14741 * all the important functionality needed for common async tasks.
14746 * it('should simulate promise', inject(function($q, $rootScope) {
14747 * var deferred = $q.defer();
14748 * var promise = deferred.promise;
14749 * var resolvedValue;
14751 * promise.then(function(value) { resolvedValue = value; });
14752 * expect(resolvedValue).toBeUndefined();
14754 * // Simulate resolving of promise
14755 * deferred.resolve(123);
14756 * // Note that the 'then' function does not get called synchronously.
14757 * // This is because we want the promise API to always be async, whether or not
14758 * // it got called synchronously or asynchronously.
14759 * expect(resolvedValue).toBeUndefined();
14761 * // Propagate promise resolution to 'then' functions using $apply().
14762 * $rootScope.$apply();
14763 * expect(resolvedValue).toEqual(123);
14767 * @param {function(function, function)} resolver Function which is responsible for resolving or
14768 * rejecting the newly created promise. The first parameter is a function which resolves the
14769 * promise, the second parameter is a function which rejects the promise.
14771 * @returns {Promise} The newly created promise.
14773 function $QProvider() {
14775 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14776 return qFactory(function(callback) {
14777 $rootScope.$evalAsync(callback);
14778 }, $exceptionHandler);
14782 function $$QProvider() {
14783 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14784 return qFactory(function(callback) {
14785 $browser.defer(callback);
14786 }, $exceptionHandler);
14791 * Constructs a promise manager.
14793 * @param {function(function)} nextTick Function for executing functions in the next turn.
14794 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14795 * debugging purposes.
14796 * @returns {object} Promise manager.
14798 function qFactory(nextTick, exceptionHandler) {
14799 var $qMinErr = minErr('$q', TypeError);
14800 function callOnce(self, resolveFn, rejectFn) {
14801 var called = false;
14802 function wrap(fn) {
14803 return function(value) {
14804 if (called) return;
14806 fn.call(self, value);
14810 return [wrap(resolveFn), wrap(rejectFn)];
14815 * @name ng.$q#defer
14819 * Creates a `Deferred` object which represents a task which will finish in the future.
14821 * @returns {Deferred} Returns a new instance of deferred.
14823 var defer = function() {
14824 return new Deferred();
14827 function Promise() {
14828 this.$$state = { status: 0 };
14831 extend(Promise.prototype, {
14832 then: function(onFulfilled, onRejected, progressBack) {
14833 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14836 var result = new Deferred();
14838 this.$$state.pending = this.$$state.pending || [];
14839 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14840 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14842 return result.promise;
14845 "catch": function(callback) {
14846 return this.then(null, callback);
14849 "finally": function(callback, progressBack) {
14850 return this.then(function(value) {
14851 return handleCallback(value, true, callback);
14852 }, function(error) {
14853 return handleCallback(error, false, callback);
14858 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14859 function simpleBind(context, fn) {
14860 return function(value) {
14861 fn.call(context, value);
14865 function processQueue(state) {
14866 var fn, deferred, pending;
14868 pending = state.pending;
14869 state.processScheduled = false;
14870 state.pending = undefined;
14871 for (var i = 0, ii = pending.length; i < ii; ++i) {
14872 deferred = pending[i][0];
14873 fn = pending[i][state.status];
14875 if (isFunction(fn)) {
14876 deferred.resolve(fn(state.value));
14877 } else if (state.status === 1) {
14878 deferred.resolve(state.value);
14880 deferred.reject(state.value);
14883 deferred.reject(e);
14884 exceptionHandler(e);
14889 function scheduleProcessQueue(state) {
14890 if (state.processScheduled || !state.pending) return;
14891 state.processScheduled = true;
14892 nextTick(function() { processQueue(state); });
14895 function Deferred() {
14896 this.promise = new Promise();
14897 //Necessary to support unbound execution :/
14898 this.resolve = simpleBind(this, this.resolve);
14899 this.reject = simpleBind(this, this.reject);
14900 this.notify = simpleBind(this, this.notify);
14903 extend(Deferred.prototype, {
14904 resolve: function(val) {
14905 if (this.promise.$$state.status) return;
14906 if (val === this.promise) {
14907 this.$$reject($qMinErr(
14909 "Expected promise to be resolved with value other than itself '{0}'",
14912 this.$$resolve(val);
14917 $$resolve: function(val) {
14920 fns = callOnce(this, this.$$resolve, this.$$reject);
14922 if ((isObject(val) || isFunction(val))) then = val && val.then;
14923 if (isFunction(then)) {
14924 this.promise.$$state.status = -1;
14925 then.call(val, fns[0], fns[1], this.notify);
14927 this.promise.$$state.value = val;
14928 this.promise.$$state.status = 1;
14929 scheduleProcessQueue(this.promise.$$state);
14933 exceptionHandler(e);
14937 reject: function(reason) {
14938 if (this.promise.$$state.status) return;
14939 this.$$reject(reason);
14942 $$reject: function(reason) {
14943 this.promise.$$state.value = reason;
14944 this.promise.$$state.status = 2;
14945 scheduleProcessQueue(this.promise.$$state);
14948 notify: function(progress) {
14949 var callbacks = this.promise.$$state.pending;
14951 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14952 nextTick(function() {
14953 var callback, result;
14954 for (var i = 0, ii = callbacks.length; i < ii; i++) {
14955 result = callbacks[i][0];
14956 callback = callbacks[i][3];
14958 result.notify(isFunction(callback) ? callback(progress) : progress);
14960 exceptionHandler(e);
14974 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14975 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14976 * a promise chain, you don't need to worry about it.
14978 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14979 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14980 * a promise error callback and you want to forward the error to the promise derived from the
14981 * current promise, you have to "rethrow" the error by returning a rejection constructed via
14985 * promiseB = promiseA.then(function(result) {
14986 * // success: do something and resolve promiseB
14987 * // with the old or a new result
14989 * }, function(reason) {
14990 * // error: handle the error if possible and
14991 * // resolve promiseB with newPromiseOrValue,
14992 * // otherwise forward the rejection to promiseB
14993 * if (canHandle(reason)) {
14994 * // handle the error and recover
14995 * return newPromiseOrValue;
14997 * return $q.reject(reason);
15001 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15002 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15004 var reject = function(reason) {
15005 var result = new Deferred();
15006 result.reject(reason);
15007 return result.promise;
15010 var makePromise = function makePromise(value, resolved) {
15011 var result = new Deferred();
15013 result.resolve(value);
15015 result.reject(value);
15017 return result.promise;
15020 var handleCallback = function handleCallback(value, isResolved, callback) {
15021 var callbackOutput = null;
15023 if (isFunction(callback)) callbackOutput = callback();
15025 return makePromise(e, false);
15027 if (isPromiseLike(callbackOutput)) {
15028 return callbackOutput.then(function() {
15029 return makePromise(value, isResolved);
15030 }, function(error) {
15031 return makePromise(error, false);
15034 return makePromise(value, isResolved);
15044 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15045 * This is useful when you are dealing with an object that might or might not be a promise, or if
15046 * the promise comes from a source that can't be trusted.
15048 * @param {*} value Value or a promise
15049 * @param {Function=} successCallback
15050 * @param {Function=} errorCallback
15051 * @param {Function=} progressCallback
15052 * @returns {Promise} Returns a promise of the passed value or promise
15056 var when = function(value, callback, errback, progressBack) {
15057 var result = new Deferred();
15058 result.resolve(value);
15059 return result.promise.then(callback, errback, progressBack);
15068 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15070 * @param {*} value Value or a promise
15071 * @param {Function=} successCallback
15072 * @param {Function=} errorCallback
15073 * @param {Function=} progressCallback
15074 * @returns {Promise} Returns a promise of the passed value or promise
15076 var resolve = when;
15084 * Combines multiple promises into a single promise that is resolved when all of the input
15085 * promises are resolved.
15087 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15088 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15089 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
15090 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
15091 * with the same rejection value.
15094 function all(promises) {
15095 var deferred = new Deferred(),
15097 results = isArray(promises) ? [] : {};
15099 forEach(promises, function(promise, key) {
15101 when(promise).then(function(value) {
15102 if (results.hasOwnProperty(key)) return;
15103 results[key] = value;
15104 if (!(--counter)) deferred.resolve(results);
15105 }, function(reason) {
15106 if (results.hasOwnProperty(key)) return;
15107 deferred.reject(reason);
15111 if (counter === 0) {
15112 deferred.resolve(results);
15115 return deferred.promise;
15118 var $Q = function Q(resolver) {
15119 if (!isFunction(resolver)) {
15120 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15123 if (!(this instanceof Q)) {
15124 // More useful when $Q is the Promise itself.
15125 return new Q(resolver);
15128 var deferred = new Deferred();
15130 function resolveFn(value) {
15131 deferred.resolve(value);
15134 function rejectFn(reason) {
15135 deferred.reject(reason);
15138 resolver(resolveFn, rejectFn);
15140 return deferred.promise;
15144 $Q.reject = reject;
15146 $Q.resolve = resolve;
15152 function $$RAFProvider() { //rAF
15153 this.$get = ['$window', '$timeout', function($window, $timeout) {
15154 var requestAnimationFrame = $window.requestAnimationFrame ||
15155 $window.webkitRequestAnimationFrame;
15157 var cancelAnimationFrame = $window.cancelAnimationFrame ||
15158 $window.webkitCancelAnimationFrame ||
15159 $window.webkitCancelRequestAnimationFrame;
15161 var rafSupported = !!requestAnimationFrame;
15162 var raf = rafSupported
15164 var id = requestAnimationFrame(fn);
15165 return function() {
15166 cancelAnimationFrame(id);
15170 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15171 return function() {
15172 $timeout.cancel(timer);
15176 raf.supported = rafSupported;
15185 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15187 * The typical use of scope is to watch the expressions, which most of the time return the same
15188 * value as last time so we optimize the operation.
15190 * Closures construction is expensive in terms of speed as well as memory:
15191 * - No closures, instead use prototypical inheritance for API
15192 * - Internal state needs to be stored on scope directly, which means that private state is
15193 * exposed as $$____ properties
15195 * Loop operations are optimized by using while(count--) { ... }
15196 * - This means that in order to keep the same order of execution as addition we have to add
15197 * items to the array at the beginning (unshift) instead of at the end (push)
15199 * Child scopes are created and removed often
15200 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15202 * There are fewer watches than observers. This is why you don't want the observer to be implemented
15203 * in the same way as watch. Watch requires return of the initialization function which is expensive
15210 * @name $rootScopeProvider
15213 * Provider for the $rootScope service.
15218 * @name $rootScopeProvider#digestTtl
15221 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15222 * assuming that the model is unstable.
15224 * The current default is 10 iterations.
15226 * In complex applications it's possible that the dependencies between `$watch`s will result in
15227 * several digest iterations. However if an application needs more than the default 10 digest
15228 * iterations for its model to stabilize then you should investigate what is causing the model to
15229 * continuously change during the digest.
15231 * Increasing the TTL could have performance implications, so you should not change it without
15232 * proper justification.
15234 * @param {number} limit The number of digest iterations.
15243 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15244 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15245 * between the model and the view, via a mechanism for watching the model for changes.
15246 * They also provide event emission/broadcast and subscription facility. See the
15247 * {@link guide/scope developer guide on scopes}.
15249 function $RootScopeProvider() {
15251 var $rootScopeMinErr = minErr('$rootScope');
15252 var lastDirtyWatch = null;
15253 var applyAsyncId = null;
15255 this.digestTtl = function(value) {
15256 if (arguments.length) {
15262 function createChildScopeClass(parent) {
15263 function ChildScope() {
15264 this.$$watchers = this.$$nextSibling =
15265 this.$$childHead = this.$$childTail = null;
15266 this.$$listeners = {};
15267 this.$$listenerCount = {};
15268 this.$$watchersCount = 0;
15269 this.$id = nextUid();
15270 this.$$ChildScope = null;
15272 ChildScope.prototype = parent;
15276 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15277 function($injector, $exceptionHandler, $parse, $browser) {
15279 function destroyChildScope($event) {
15280 $event.currentScope.$$destroyed = true;
15283 function cleanUpScope($scope) {
15286 // There is a memory leak in IE9 if all child scopes are not disconnected
15287 // completely when a scope is destroyed. So this code will recurse up through
15288 // all this scopes children
15290 // See issue https://github.com/angular/angular.js/issues/10706
15291 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15292 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15295 // The code below works around IE9 and V8's memory leaks
15298 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15299 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15300 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15302 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15303 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15308 * @name $rootScope.Scope
15311 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15312 * {@link auto.$injector $injector}. Child scopes are created using the
15313 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15314 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15315 * an in-depth introduction and usage examples.
15319 * A scope can inherit from a parent scope, as in this example:
15321 var parent = $rootScope;
15322 var child = parent.$new();
15324 parent.salutation = "Hello";
15325 expect(child.salutation).toEqual('Hello');
15327 child.salutation = "Welcome";
15328 expect(child.salutation).toEqual('Welcome');
15329 expect(parent.salutation).toEqual('Hello');
15332 * When interacting with `Scope` in tests, additional helper methods are available on the
15333 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15337 * @param {Object.<string, function()>=} providers Map of service factory which need to be
15338 * provided for the current scope. Defaults to {@link ng}.
15339 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15340 * append/override services provided by `providers`. This is handy
15341 * when unit-testing and having the need to override a default
15343 * @returns {Object} Newly created scope.
15347 this.$id = nextUid();
15348 this.$$phase = this.$parent = this.$$watchers =
15349 this.$$nextSibling = this.$$prevSibling =
15350 this.$$childHead = this.$$childTail = null;
15352 this.$$destroyed = false;
15353 this.$$listeners = {};
15354 this.$$listenerCount = {};
15355 this.$$watchersCount = 0;
15356 this.$$isolateBindings = null;
15361 * @name $rootScope.Scope#$id
15364 * Unique scope ID (monotonically increasing) useful for debugging.
15369 * @name $rootScope.Scope#$parent
15372 * Reference to the parent scope.
15377 * @name $rootScope.Scope#$root
15380 * Reference to the root scope.
15383 Scope.prototype = {
15384 constructor: Scope,
15387 * @name $rootScope.Scope#$new
15391 * Creates a new child {@link ng.$rootScope.Scope scope}.
15393 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15394 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15396 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15397 * desired for the scope and its child scopes to be permanently detached from the parent and
15398 * thus stop participating in model change detection and listener notification by invoking.
15400 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15401 * parent scope. The scope is isolated, as it can not see parent scope properties.
15402 * When creating widgets, it is useful for the widget to not accidentally read parent
15405 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15406 * of the newly created scope. Defaults to `this` scope if not provided.
15407 * This is used when creating a transclude scope to correctly place it
15408 * in the scope hierarchy while maintaining the correct prototypical
15411 * @returns {Object} The newly created child scope.
15414 $new: function(isolate, parent) {
15417 parent = parent || this;
15420 child = new Scope();
15421 child.$root = this.$root;
15423 // Only create a child scope class if somebody asks for one,
15424 // but cache it to allow the VM to optimize lookups.
15425 if (!this.$$ChildScope) {
15426 this.$$ChildScope = createChildScopeClass(this);
15428 child = new this.$$ChildScope();
15430 child.$parent = parent;
15431 child.$$prevSibling = parent.$$childTail;
15432 if (parent.$$childHead) {
15433 parent.$$childTail.$$nextSibling = child;
15434 parent.$$childTail = child;
15436 parent.$$childHead = parent.$$childTail = child;
15439 // When the new scope is not isolated or we inherit from `this`, and
15440 // the parent scope is destroyed, the property `$$destroyed` is inherited
15441 // prototypically. In all other cases, this property needs to be set
15442 // when the parent scope is destroyed.
15443 // The listener needs to be added after the parent is set
15444 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15451 * @name $rootScope.Scope#$watch
15455 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15457 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15458 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
15459 * its value when executed multiple times with the same input because it may be executed multiple
15460 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15461 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15462 * - The `listener` is called only when the value from the current `watchExpression` and the
15463 * previous call to `watchExpression` are not equal (with the exception of the initial run,
15464 * see below). Inequality is determined according to reference inequality,
15465 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15466 * via the `!==` Javascript operator, unless `objectEquality == true`
15468 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15469 * according to the {@link angular.equals} function. To save the value of the object for
15470 * later comparison, the {@link angular.copy} function is used. This therefore means that
15471 * watching complex objects will have adverse memory and performance implications.
15472 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15473 * This is achieved by rerunning the watchers until no changes are detected. The rerun
15474 * iteration limit is 10 to prevent an infinite loop deadlock.
15477 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15478 * you can register a `watchExpression` function with no `listener`. (Be prepared for
15479 * multiple calls to your `watchExpression` because it will execute multiple times in a
15480 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15482 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15483 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15484 * watcher. In rare cases, this is undesirable because the listener is called when the result
15485 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15486 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15487 * listener was called due to initialization.
15493 // let's assume that scope was dependency injected as the $rootScope
15494 var scope = $rootScope;
15495 scope.name = 'misko';
15498 expect(scope.counter).toEqual(0);
15499 scope.$watch('name', function(newValue, oldValue) {
15500 scope.counter = scope.counter + 1;
15502 expect(scope.counter).toEqual(0);
15505 // the listener is always called during the first $digest loop after it was registered
15506 expect(scope.counter).toEqual(1);
15509 // but now it will not be called unless the value changes
15510 expect(scope.counter).toEqual(1);
15512 scope.name = 'adam';
15514 expect(scope.counter).toEqual(2);
15518 // Using a function as a watchExpression
15520 scope.foodCounter = 0;
15521 expect(scope.foodCounter).toEqual(0);
15523 // This function returns the value being watched. It is called for each turn of the $digest loop
15524 function() { return food; },
15525 // This is the change listener, called when the value returned from the above function changes
15526 function(newValue, oldValue) {
15527 if ( newValue !== oldValue ) {
15528 // Only increment the counter if the value changed
15529 scope.foodCounter = scope.foodCounter + 1;
15533 // No digest has been run so the counter will be zero
15534 expect(scope.foodCounter).toEqual(0);
15536 // Run the digest but since food has not changed count will still be zero
15538 expect(scope.foodCounter).toEqual(0);
15540 // Update food and run digest. Now the counter will increment
15541 food = 'cheeseburger';
15543 expect(scope.foodCounter).toEqual(1);
15549 * @param {(function()|string)} watchExpression Expression that is evaluated on each
15550 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15551 * a call to the `listener`.
15553 * - `string`: Evaluated as {@link guide/expression expression}
15554 * - `function(scope)`: called with current `scope` as a parameter.
15555 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15556 * of `watchExpression` changes.
15558 * - `newVal` contains the current value of the `watchExpression`
15559 * - `oldVal` contains the previous value of the `watchExpression`
15560 * - `scope` refers to the current scope
15561 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15562 * comparing for reference equality.
15563 * @returns {function()} Returns a deregistration function for this listener.
15565 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15566 var get = $parse(watchExp);
15568 if (get.$$watchDelegate) {
15569 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15572 array = scope.$$watchers,
15575 last: initWatchVal,
15577 exp: prettyPrintExpression || watchExp,
15578 eq: !!objectEquality
15581 lastDirtyWatch = null;
15583 if (!isFunction(listener)) {
15588 array = scope.$$watchers = [];
15590 // we use unshift since we use a while loop in $digest for speed.
15591 // the while loop reads in reverse order.
15592 array.unshift(watcher);
15593 incrementWatchersCount(this, 1);
15595 return function deregisterWatch() {
15596 if (arrayRemove(array, watcher) >= 0) {
15597 incrementWatchersCount(scope, -1);
15599 lastDirtyWatch = null;
15605 * @name $rootScope.Scope#$watchGroup
15609 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15610 * If any one expression in the collection changes the `listener` is executed.
15612 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15613 * call to $digest() to see if any items changes.
15614 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15616 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15617 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15619 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15620 * expression in `watchExpressions` changes
15621 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15622 * those of `watchExpression`
15623 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15624 * those of `watchExpression`
15625 * The `scope` refers to the current scope.
15626 * @returns {function()} Returns a de-registration function for all listeners.
15628 $watchGroup: function(watchExpressions, listener) {
15629 var oldValues = new Array(watchExpressions.length);
15630 var newValues = new Array(watchExpressions.length);
15631 var deregisterFns = [];
15633 var changeReactionScheduled = false;
15634 var firstRun = true;
15636 if (!watchExpressions.length) {
15637 // No expressions means we call the listener ASAP
15638 var shouldCall = true;
15639 self.$evalAsync(function() {
15640 if (shouldCall) listener(newValues, newValues, self);
15642 return function deregisterWatchGroup() {
15643 shouldCall = false;
15647 if (watchExpressions.length === 1) {
15648 // Special case size of one
15649 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15650 newValues[0] = value;
15651 oldValues[0] = oldValue;
15652 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15656 forEach(watchExpressions, function(expr, i) {
15657 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15658 newValues[i] = value;
15659 oldValues[i] = oldValue;
15660 if (!changeReactionScheduled) {
15661 changeReactionScheduled = true;
15662 self.$evalAsync(watchGroupAction);
15665 deregisterFns.push(unwatchFn);
15668 function watchGroupAction() {
15669 changeReactionScheduled = false;
15673 listener(newValues, newValues, self);
15675 listener(newValues, oldValues, self);
15679 return function deregisterWatchGroup() {
15680 while (deregisterFns.length) {
15681 deregisterFns.shift()();
15689 * @name $rootScope.Scope#$watchCollection
15693 * Shallow watches the properties of an object and fires whenever any of the properties change
15694 * (for arrays, this implies watching the array items; for object maps, this implies watching
15695 * the properties). If a change is detected, the `listener` callback is fired.
15697 * - The `obj` collection is observed via standard $watch operation and is examined on every
15698 * call to $digest() to see if any items have been added, removed, or moved.
15699 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15700 * adding, removing, and moving items belonging to an object or array.
15705 $scope.names = ['igor', 'matias', 'misko', 'james'];
15706 $scope.dataCount = 4;
15708 $scope.$watchCollection('names', function(newNames, oldNames) {
15709 $scope.dataCount = newNames.length;
15712 expect($scope.dataCount).toEqual(4);
15715 //still at 4 ... no changes
15716 expect($scope.dataCount).toEqual(4);
15718 $scope.names.pop();
15721 //now there's been a change
15722 expect($scope.dataCount).toEqual(3);
15726 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15727 * expression value should evaluate to an object or an array which is observed on each
15728 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15729 * collection will trigger a call to the `listener`.
15731 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15732 * when a change is detected.
15733 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
15734 * - The `oldCollection` object is a copy of the former collection data.
15735 * Due to performance considerations, the`oldCollection` value is computed only if the
15736 * `listener` function declares two or more arguments.
15737 * - The `scope` argument refers to the current scope.
15739 * @returns {function()} Returns a de-registration function for this listener. When the
15740 * de-registration function is executed, the internal watch operation is terminated.
15742 $watchCollection: function(obj, listener) {
15743 $watchCollectionInterceptor.$stateful = true;
15746 // the current value, updated on each dirty-check run
15748 // a shallow copy of the newValue from the last dirty-check run,
15749 // updated to match newValue during dirty-check run
15751 // a shallow copy of the newValue from when the last change happened
15753 // only track veryOldValue if the listener is asking for it
15754 var trackVeryOldValue = (listener.length > 1);
15755 var changeDetected = 0;
15756 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15757 var internalArray = [];
15758 var internalObject = {};
15759 var initRun = true;
15762 function $watchCollectionInterceptor(_value) {
15764 var newLength, key, bothNaN, newItem, oldItem;
15766 // If the new value is undefined, then return undefined as the watch may be a one-time watch
15767 if (isUndefined(newValue)) return;
15769 if (!isObject(newValue)) { // if primitive
15770 if (oldValue !== newValue) {
15771 oldValue = newValue;
15774 } else if (isArrayLike(newValue)) {
15775 if (oldValue !== internalArray) {
15776 // we are transitioning from something which was not an array into array.
15777 oldValue = internalArray;
15778 oldLength = oldValue.length = 0;
15782 newLength = newValue.length;
15784 if (oldLength !== newLength) {
15785 // if lengths do not match we need to trigger change notification
15787 oldValue.length = oldLength = newLength;
15789 // copy the items to oldValue and look for changes.
15790 for (var i = 0; i < newLength; i++) {
15791 oldItem = oldValue[i];
15792 newItem = newValue[i];
15794 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15795 if (!bothNaN && (oldItem !== newItem)) {
15797 oldValue[i] = newItem;
15801 if (oldValue !== internalObject) {
15802 // we are transitioning from something which was not an object into object.
15803 oldValue = internalObject = {};
15807 // copy the items to oldValue and look for changes.
15809 for (key in newValue) {
15810 if (hasOwnProperty.call(newValue, key)) {
15812 newItem = newValue[key];
15813 oldItem = oldValue[key];
15815 if (key in oldValue) {
15816 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15817 if (!bothNaN && (oldItem !== newItem)) {
15819 oldValue[key] = newItem;
15823 oldValue[key] = newItem;
15828 if (oldLength > newLength) {
15829 // we used to have more keys, need to find them and destroy them.
15831 for (key in oldValue) {
15832 if (!hasOwnProperty.call(newValue, key)) {
15834 delete oldValue[key];
15839 return changeDetected;
15842 function $watchCollectionAction() {
15845 listener(newValue, newValue, self);
15847 listener(newValue, veryOldValue, self);
15850 // make a copy for the next time a collection is changed
15851 if (trackVeryOldValue) {
15852 if (!isObject(newValue)) {
15854 veryOldValue = newValue;
15855 } else if (isArrayLike(newValue)) {
15856 veryOldValue = new Array(newValue.length);
15857 for (var i = 0; i < newValue.length; i++) {
15858 veryOldValue[i] = newValue[i];
15860 } else { // if object
15862 for (var key in newValue) {
15863 if (hasOwnProperty.call(newValue, key)) {
15864 veryOldValue[key] = newValue[key];
15871 return this.$watch(changeDetector, $watchCollectionAction);
15876 * @name $rootScope.Scope#$digest
15880 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15881 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15882 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15883 * until no more listeners are firing. This means that it is possible to get into an infinite
15884 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15885 * iterations exceeds 10.
15887 * Usually, you don't call `$digest()` directly in
15888 * {@link ng.directive:ngController controllers} or in
15889 * {@link ng.$compileProvider#directive directives}.
15890 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15891 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15893 * If you want to be notified whenever `$digest()` is called,
15894 * you can register a `watchExpression` function with
15895 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15897 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15902 scope.name = 'misko';
15905 expect(scope.counter).toEqual(0);
15906 scope.$watch('name', function(newValue, oldValue) {
15907 scope.counter = scope.counter + 1;
15909 expect(scope.counter).toEqual(0);
15912 // the listener is always called during the first $digest loop after it was registered
15913 expect(scope.counter).toEqual(1);
15916 // but now it will not be called unless the value changes
15917 expect(scope.counter).toEqual(1);
15919 scope.name = 'adam';
15921 expect(scope.counter).toEqual(2);
15925 $digest: function() {
15926 var watch, value, last,
15930 next, current, target = this,
15932 logIdx, logMsg, asyncTask;
15934 beginPhase('$digest');
15935 // Check for changes to browser url that happened in sync before the call to $digest
15936 $browser.$$checkUrlChange();
15938 if (this === $rootScope && applyAsyncId !== null) {
15939 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15940 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15941 $browser.defer.cancel(applyAsyncId);
15945 lastDirtyWatch = null;
15947 do { // "while dirty" loop
15951 while (asyncQueue.length) {
15953 asyncTask = asyncQueue.shift();
15954 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15956 $exceptionHandler(e);
15958 lastDirtyWatch = null;
15961 traverseScopesLoop:
15962 do { // "traverse the scopes" loop
15963 if ((watchers = current.$$watchers)) {
15964 // process our watches
15965 length = watchers.length;
15968 watch = watchers[length];
15969 // Most common watches are on primitives, in which case we can short
15970 // circuit it with === operator, only when === fails do we use .equals
15972 if ((value = watch.get(current)) !== (last = watch.last) &&
15974 ? equals(value, last)
15975 : (typeof value === 'number' && typeof last === 'number'
15976 && isNaN(value) && isNaN(last)))) {
15978 lastDirtyWatch = watch;
15979 watch.last = watch.eq ? copy(value, null) : value;
15980 watch.fn(value, ((last === initWatchVal) ? value : last), current);
15983 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15984 watchLog[logIdx].push({
15985 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15990 } else if (watch === lastDirtyWatch) {
15991 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15992 // have already been tested.
15994 break traverseScopesLoop;
15998 $exceptionHandler(e);
16003 // Insanity Warning: scope depth-first traversal
16004 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16005 // this piece should be kept in sync with the traversal in $broadcast
16006 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16007 (current !== target && current.$$nextSibling)))) {
16008 while (current !== target && !(next = current.$$nextSibling)) {
16009 current = current.$parent;
16012 } while ((current = next));
16014 // `break traverseScopesLoop;` takes us to here
16016 if ((dirty || asyncQueue.length) && !(ttl--)) {
16018 throw $rootScopeMinErr('infdig',
16019 '{0} $digest() iterations reached. Aborting!\n' +
16020 'Watchers fired in the last 5 iterations: {1}',
16024 } while (dirty || asyncQueue.length);
16028 while (postDigestQueue.length) {
16030 postDigestQueue.shift()();
16032 $exceptionHandler(e);
16040 * @name $rootScope.Scope#$destroy
16041 * @eventType broadcast on scope being destroyed
16044 * Broadcasted when a scope and its children are being destroyed.
16046 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16047 * clean up DOM bindings before an element is removed from the DOM.
16052 * @name $rootScope.Scope#$destroy
16056 * Removes the current scope (and all of its children) from the parent scope. Removal implies
16057 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16058 * propagate to the current scope and its children. Removal also implies that the current
16059 * scope is eligible for garbage collection.
16061 * The `$destroy()` is usually used by directives such as
16062 * {@link ng.directive:ngRepeat ngRepeat} for managing the
16063 * unrolling of the loop.
16065 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16066 * Application code can register a `$destroy` event handler that will give it a chance to
16067 * perform any necessary cleanup.
16069 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16070 * clean up DOM bindings before an element is removed from the DOM.
16072 $destroy: function() {
16073 // We can't destroy a scope that has been already destroyed.
16074 if (this.$$destroyed) return;
16075 var parent = this.$parent;
16077 this.$broadcast('$destroy');
16078 this.$$destroyed = true;
16080 if (this === $rootScope) {
16081 //Remove handlers attached to window when $rootScope is removed
16082 $browser.$$applicationDestroyed();
16085 incrementWatchersCount(this, -this.$$watchersCount);
16086 for (var eventName in this.$$listenerCount) {
16087 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16090 // sever all the references to parent scopes (after this cleanup, the current scope should
16091 // not be retained by any of our references and should be eligible for garbage collection)
16092 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16093 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16094 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16095 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16097 // Disable listeners, watchers and apply/digest methods
16098 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16099 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16100 this.$$listeners = {};
16102 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16103 this.$$nextSibling = null;
16104 cleanUpScope(this);
16109 * @name $rootScope.Scope#$eval
16113 * Executes the `expression` on the current scope and returns the result. Any exceptions in
16114 * the expression are propagated (uncaught). This is useful when evaluating Angular
16119 var scope = ng.$rootScope.Scope();
16123 expect(scope.$eval('a+b')).toEqual(3);
16124 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16127 * @param {(string|function())=} expression An angular expression to be executed.
16129 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16130 * - `function(scope)`: execute the function with the current `scope` parameter.
16132 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16133 * @returns {*} The result of evaluating the expression.
16135 $eval: function(expr, locals) {
16136 return $parse(expr)(this, locals);
16141 * @name $rootScope.Scope#$evalAsync
16145 * Executes the expression on the current scope at a later point in time.
16147 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16150 * - it will execute after the function that scheduled the evaluation (preferably before DOM
16152 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16153 * `expression` execution.
16155 * Any exceptions from the execution of the expression are forwarded to the
16156 * {@link ng.$exceptionHandler $exceptionHandler} service.
16158 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16159 * will be scheduled. However, it is encouraged to always call code that changes the model
16160 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16162 * @param {(string|function())=} expression An angular expression to be executed.
16164 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16165 * - `function(scope)`: execute the function with the current `scope` parameter.
16167 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16169 $evalAsync: function(expr, locals) {
16170 // if we are outside of an $digest loop and this is the first time we are scheduling async
16171 // task also schedule async auto-flush
16172 if (!$rootScope.$$phase && !asyncQueue.length) {
16173 $browser.defer(function() {
16174 if (asyncQueue.length) {
16175 $rootScope.$digest();
16180 asyncQueue.push({scope: this, expression: expr, locals: locals});
16183 $$postDigest: function(fn) {
16184 postDigestQueue.push(fn);
16189 * @name $rootScope.Scope#$apply
16193 * `$apply()` is used to execute an expression in angular from outside of the angular
16194 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16195 * Because we are calling into the angular framework we need to perform proper scope life
16196 * cycle of {@link ng.$exceptionHandler exception handling},
16197 * {@link ng.$rootScope.Scope#$digest executing watches}.
16201 * # Pseudo-Code of `$apply()`
16203 function $apply(expr) {
16205 return $eval(expr);
16207 $exceptionHandler(e);
16215 * Scope's `$apply()` method transitions through the following stages:
16217 * 1. The {@link guide/expression expression} is executed using the
16218 * {@link ng.$rootScope.Scope#$eval $eval()} method.
16219 * 2. Any exceptions from the execution of the expression are forwarded to the
16220 * {@link ng.$exceptionHandler $exceptionHandler} service.
16221 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16222 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16225 * @param {(string|function())=} exp An angular expression to be executed.
16227 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16228 * - `function(scope)`: execute the function with current `scope` parameter.
16230 * @returns {*} The result of evaluating the expression.
16232 $apply: function(expr) {
16234 beginPhase('$apply');
16236 return this.$eval(expr);
16241 $exceptionHandler(e);
16244 $rootScope.$digest();
16246 $exceptionHandler(e);
16254 * @name $rootScope.Scope#$applyAsync
16258 * Schedule the invocation of $apply to occur at a later time. The actual time difference
16259 * varies across browsers, but is typically around ~10 milliseconds.
16261 * This can be used to queue up multiple expressions which need to be evaluated in the same
16264 * @param {(string|function())=} exp An angular expression to be executed.
16266 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16267 * - `function(scope)`: execute the function with current `scope` parameter.
16269 $applyAsync: function(expr) {
16271 expr && applyAsyncQueue.push($applyAsyncExpression);
16272 scheduleApplyAsync();
16274 function $applyAsyncExpression() {
16281 * @name $rootScope.Scope#$on
16285 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16286 * discussion of event life cycle.
16288 * The event listener function format is: `function(event, args...)`. The `event` object
16289 * passed into the listener has the following attributes:
16291 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16293 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16294 * event propagates through the scope hierarchy, this property is set to null.
16295 * - `name` - `{string}`: name of the event.
16296 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16297 * further event propagation (available only for events that were `$emit`-ed).
16298 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16300 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16302 * @param {string} name Event name to listen on.
16303 * @param {function(event, ...args)} listener Function to call when the event is emitted.
16304 * @returns {function()} Returns a deregistration function for this listener.
16306 $on: function(name, listener) {
16307 var namedListeners = this.$$listeners[name];
16308 if (!namedListeners) {
16309 this.$$listeners[name] = namedListeners = [];
16311 namedListeners.push(listener);
16313 var current = this;
16315 if (!current.$$listenerCount[name]) {
16316 current.$$listenerCount[name] = 0;
16318 current.$$listenerCount[name]++;
16319 } while ((current = current.$parent));
16322 return function() {
16323 var indexOfListener = namedListeners.indexOf(listener);
16324 if (indexOfListener !== -1) {
16325 namedListeners[indexOfListener] = null;
16326 decrementListenerCount(self, 1, name);
16334 * @name $rootScope.Scope#$emit
16338 * Dispatches an event `name` upwards through the scope hierarchy notifying the
16339 * registered {@link ng.$rootScope.Scope#$on} listeners.
16341 * The event life cycle starts at the scope on which `$emit` was called. All
16342 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16343 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16344 * registered listeners along the way. The event will stop propagating if one of the listeners
16347 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16348 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16350 * @param {string} name Event name to emit.
16351 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16352 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16354 $emit: function(name, args) {
16358 stopPropagation = false,
16361 targetScope: scope,
16362 stopPropagation: function() {stopPropagation = true;},
16363 preventDefault: function() {
16364 event.defaultPrevented = true;
16366 defaultPrevented: false
16368 listenerArgs = concat([event], arguments, 1),
16372 namedListeners = scope.$$listeners[name] || empty;
16373 event.currentScope = scope;
16374 for (i = 0, length = namedListeners.length; i < length; i++) {
16376 // if listeners were deregistered, defragment the array
16377 if (!namedListeners[i]) {
16378 namedListeners.splice(i, 1);
16384 //allow all listeners attached to the current scope to run
16385 namedListeners[i].apply(null, listenerArgs);
16387 $exceptionHandler(e);
16390 //if any listener on the current scope stops propagation, prevent bubbling
16391 if (stopPropagation) {
16392 event.currentScope = null;
16396 scope = scope.$parent;
16399 event.currentScope = null;
16407 * @name $rootScope.Scope#$broadcast
16411 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16412 * registered {@link ng.$rootScope.Scope#$on} listeners.
16414 * The event life cycle starts at the scope on which `$broadcast` was called. All
16415 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16416 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16417 * scope and calls all registered listeners along the way. The event cannot be canceled.
16419 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16420 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16422 * @param {string} name Event name to broadcast.
16423 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16424 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16426 $broadcast: function(name, args) {
16432 targetScope: target,
16433 preventDefault: function() {
16434 event.defaultPrevented = true;
16436 defaultPrevented: false
16439 if (!target.$$listenerCount[name]) return event;
16441 var listenerArgs = concat([event], arguments, 1),
16442 listeners, i, length;
16444 //down while you can, then up and next sibling or up and next sibling until back at root
16445 while ((current = next)) {
16446 event.currentScope = current;
16447 listeners = current.$$listeners[name] || [];
16448 for (i = 0, length = listeners.length; i < length; i++) {
16449 // if listeners were deregistered, defragment the array
16450 if (!listeners[i]) {
16451 listeners.splice(i, 1);
16458 listeners[i].apply(null, listenerArgs);
16460 $exceptionHandler(e);
16464 // Insanity Warning: scope depth-first traversal
16465 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16466 // this piece should be kept in sync with the traversal in $digest
16467 // (though it differs due to having the extra check for $$listenerCount)
16468 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16469 (current !== target && current.$$nextSibling)))) {
16470 while (current !== target && !(next = current.$$nextSibling)) {
16471 current = current.$parent;
16476 event.currentScope = null;
16481 var $rootScope = new Scope();
16483 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16484 var asyncQueue = $rootScope.$$asyncQueue = [];
16485 var postDigestQueue = $rootScope.$$postDigestQueue = [];
16486 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16491 function beginPhase(phase) {
16492 if ($rootScope.$$phase) {
16493 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16496 $rootScope.$$phase = phase;
16499 function clearPhase() {
16500 $rootScope.$$phase = null;
16503 function incrementWatchersCount(current, count) {
16505 current.$$watchersCount += count;
16506 } while ((current = current.$parent));
16509 function decrementListenerCount(current, count, name) {
16511 current.$$listenerCount[name] -= count;
16513 if (current.$$listenerCount[name] === 0) {
16514 delete current.$$listenerCount[name];
16516 } while ((current = current.$parent));
16520 * function used as an initial value for watchers.
16521 * because it's unique we can easily tell it apart from other values
16523 function initWatchVal() {}
16525 function flushApplyAsync() {
16526 while (applyAsyncQueue.length) {
16528 applyAsyncQueue.shift()();
16530 $exceptionHandler(e);
16533 applyAsyncId = null;
16536 function scheduleApplyAsync() {
16537 if (applyAsyncId === null) {
16538 applyAsyncId = $browser.defer(function() {
16539 $rootScope.$apply(flushApplyAsync);
16548 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16550 function $$SanitizeUriProvider() {
16551 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16552 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16556 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16557 * urls during a[href] sanitization.
16559 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16561 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16562 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16563 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16564 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16566 * @param {RegExp=} regexp New regexp to whitelist urls with.
16567 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16568 * chaining otherwise.
16570 this.aHrefSanitizationWhitelist = function(regexp) {
16571 if (isDefined(regexp)) {
16572 aHrefSanitizationWhitelist = regexp;
16575 return aHrefSanitizationWhitelist;
16581 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16582 * urls during img[src] sanitization.
16584 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16586 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16587 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16588 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16589 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16591 * @param {RegExp=} regexp New regexp to whitelist urls with.
16592 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16593 * chaining otherwise.
16595 this.imgSrcSanitizationWhitelist = function(regexp) {
16596 if (isDefined(regexp)) {
16597 imgSrcSanitizationWhitelist = regexp;
16600 return imgSrcSanitizationWhitelist;
16603 this.$get = function() {
16604 return function sanitizeUri(uri, isImage) {
16605 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16607 normalizedVal = urlResolve(uri).href;
16608 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16609 return 'unsafe:' + normalizedVal;
16616 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16617 * Any commits to this file should be reviewed with security in mind. *
16618 * Changes to this file can potentially create security vulnerabilities. *
16619 * An approval from 2 Core members with history of modifying *
16620 * this file is required. *
16622 * Does the change somehow allow for arbitrary javascript to be executed? *
16623 * Or allows for someone to change the prototype of built-in objects? *
16624 * Or gives undesired access to variables likes document or window? *
16625 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16627 var $sceMinErr = minErr('$sce');
16629 var SCE_CONTEXTS = {
16633 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16634 // url. (e.g. ng-include, script src, templateUrl)
16635 RESOURCE_URL: 'resourceUrl',
16639 // Helper functions follow.
16641 function adjustMatcher(matcher) {
16642 if (matcher === 'self') {
16644 } else if (isString(matcher)) {
16645 // Strings match exactly except for 2 wildcards - '*' and '**'.
16646 // '*' matches any character except those from the set ':/.?&'.
16647 // '**' matches any character (like .* in a RegExp).
16648 // More than 2 *'s raises an error as it's ill defined.
16649 if (matcher.indexOf('***') > -1) {
16650 throw $sceMinErr('iwcard',
16651 'Illegal sequence *** in string matcher. String: {0}', matcher);
16653 matcher = escapeForRegexp(matcher).
16654 replace('\\*\\*', '.*').
16655 replace('\\*', '[^:/.?&;]*');
16656 return new RegExp('^' + matcher + '$');
16657 } else if (isRegExp(matcher)) {
16658 // The only other type of matcher allowed is a Regexp.
16659 // Match entire URL / disallow partial matches.
16660 // Flags are reset (i.e. no global, ignoreCase or multiline)
16661 return new RegExp('^' + matcher.source + '$');
16663 throw $sceMinErr('imatcher',
16664 'Matchers may only be "self", string patterns or RegExp objects');
16669 function adjustMatchers(matchers) {
16670 var adjustedMatchers = [];
16671 if (isDefined(matchers)) {
16672 forEach(matchers, function(matcher) {
16673 adjustedMatchers.push(adjustMatcher(matcher));
16676 return adjustedMatchers;
16682 * @name $sceDelegate
16687 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16688 * Contextual Escaping (SCE)} services to AngularJS.
16690 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16691 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
16692 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16693 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16694 * work because `$sce` delegates to `$sceDelegate` for these operations.
16696 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16698 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
16699 * can override it completely to change the behavior of `$sce`, the common case would
16700 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16701 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16702 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16703 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16704 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16709 * @name $sceDelegateProvider
16712 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16713 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
16714 * that the URLs used for sourcing Angular templates are safe. Refer {@link
16715 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16716 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16718 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16719 * Strict Contextual Escaping (SCE)}.
16721 * **Example**: Consider the following case. <a name="example"></a>
16723 * - your app is hosted at url `http://myapp.example.com/`
16724 * - but some of your templates are hosted on other domains you control such as
16725 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
16726 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16728 * Here is what a secure configuration for this scenario might look like:
16731 * angular.module('myApp', []).config(function($sceDelegateProvider) {
16732 * $sceDelegateProvider.resourceUrlWhitelist([
16733 * // Allow same origin resource loads.
16735 * // Allow loading from our assets domain. Notice the difference between * and **.
16736 * 'http://srv*.assets.example.com/**'
16739 * // The blacklist overrides the whitelist so the open redirect here is blocked.
16740 * $sceDelegateProvider.resourceUrlBlacklist([
16741 * 'http://myapp.example.com/clickThru**'
16747 function $SceDelegateProvider() {
16748 this.SCE_CONTEXTS = SCE_CONTEXTS;
16750 // Resource URLs can also be trusted by policy.
16751 var resourceUrlWhitelist = ['self'],
16752 resourceUrlBlacklist = [];
16756 * @name $sceDelegateProvider#resourceUrlWhitelist
16759 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16760 * provided. This must be an array or null. A snapshot of this array is used so further
16761 * changes to the array are ignored.
16763 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16764 * allowed in this array.
16766 * Note: **an empty whitelist array will block all URLs**!
16768 * @return {Array} the currently set whitelist array.
16770 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16771 * same origin resource requests.
16774 * Sets/Gets the whitelist of trusted resource URLs.
16776 this.resourceUrlWhitelist = function(value) {
16777 if (arguments.length) {
16778 resourceUrlWhitelist = adjustMatchers(value);
16780 return resourceUrlWhitelist;
16785 * @name $sceDelegateProvider#resourceUrlBlacklist
16788 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16789 * provided. This must be an array or null. A snapshot of this array is used so further
16790 * changes to the array are ignored.
16792 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16793 * allowed in this array.
16795 * The typical usage for the blacklist is to **block
16796 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16797 * these would otherwise be trusted but actually return content from the redirected domain.
16799 * Finally, **the blacklist overrides the whitelist** and has the final say.
16801 * @return {Array} the currently set blacklist array.
16803 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16804 * is no blacklist.)
16807 * Sets/Gets the blacklist of trusted resource URLs.
16810 this.resourceUrlBlacklist = function(value) {
16811 if (arguments.length) {
16812 resourceUrlBlacklist = adjustMatchers(value);
16814 return resourceUrlBlacklist;
16817 this.$get = ['$injector', function($injector) {
16819 var htmlSanitizer = function htmlSanitizer(html) {
16820 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16823 if ($injector.has('$sanitize')) {
16824 htmlSanitizer = $injector.get('$sanitize');
16828 function matchUrl(matcher, parsedUrl) {
16829 if (matcher === 'self') {
16830 return urlIsSameOrigin(parsedUrl);
16832 // definitely a regex. See adjustMatchers()
16833 return !!matcher.exec(parsedUrl.href);
16837 function isResourceUrlAllowedByPolicy(url) {
16838 var parsedUrl = urlResolve(url.toString());
16839 var i, n, allowed = false;
16840 // Ensure that at least one item from the whitelist allows this url.
16841 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16842 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16848 // Ensure that no item from the blacklist blocked this url.
16849 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16850 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16859 function generateHolderType(Base) {
16860 var holderType = function TrustedValueHolderType(trustedValue) {
16861 this.$$unwrapTrustedValue = function() {
16862 return trustedValue;
16866 holderType.prototype = new Base();
16868 holderType.prototype.valueOf = function sceValueOf() {
16869 return this.$$unwrapTrustedValue();
16871 holderType.prototype.toString = function sceToString() {
16872 return this.$$unwrapTrustedValue().toString();
16877 var trustedValueHolderBase = generateHolderType(),
16880 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16881 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16882 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16883 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16884 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16888 * @name $sceDelegate#trustAs
16891 * Returns an object that is trusted by angular for use in specified strict
16892 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16893 * attribute interpolation, any dom event binding attribute interpolation
16894 * such as for onclick, etc.) that uses the provided value.
16895 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16897 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
16898 * resourceUrl, html, js and css.
16899 * @param {*} value The value that that should be considered trusted/safe.
16900 * @returns {*} A value that can be used to stand in for the provided `value` in places
16901 * where Angular expects a $sce.trustAs() return value.
16903 function trustAs(type, trustedValue) {
16904 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16905 if (!Constructor) {
16906 throw $sceMinErr('icontext',
16907 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16908 type, trustedValue);
16910 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16911 return trustedValue;
16913 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
16914 // mutable objects, we ensure here that the value passed in is actually a string.
16915 if (typeof trustedValue !== 'string') {
16916 throw $sceMinErr('itype',
16917 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16920 return new Constructor(trustedValue);
16925 * @name $sceDelegate#valueOf
16928 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16929 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16930 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16932 * If the passed parameter is not a value that had been returned by {@link
16933 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16935 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16936 * call or anything else.
16937 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16938 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
16939 * `value` unchanged.
16941 function valueOf(maybeTrusted) {
16942 if (maybeTrusted instanceof trustedValueHolderBase) {
16943 return maybeTrusted.$$unwrapTrustedValue();
16945 return maybeTrusted;
16951 * @name $sceDelegate#getTrusted
16954 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16955 * returns the originally supplied value if the queried context type is a supertype of the
16956 * created type. If this condition isn't satisfied, throws an exception.
16958 * @param {string} type The kind of context in which this value is to be used.
16959 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16960 * `$sceDelegate.trustAs`} call.
16961 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16962 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
16964 function getTrusted(type, maybeTrusted) {
16965 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16966 return maybeTrusted;
16968 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16969 if (constructor && maybeTrusted instanceof constructor) {
16970 return maybeTrusted.$$unwrapTrustedValue();
16972 // If we get here, then we may only take one of two actions.
16973 // 1. sanitize the value for the requested type, or
16974 // 2. throw an exception.
16975 if (type === SCE_CONTEXTS.RESOURCE_URL) {
16976 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16977 return maybeTrusted;
16979 throw $sceMinErr('insecurl',
16980 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
16981 maybeTrusted.toString());
16983 } else if (type === SCE_CONTEXTS.HTML) {
16984 return htmlSanitizer(maybeTrusted);
16986 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16989 return { trustAs: trustAs,
16990 getTrusted: getTrusted,
16991 valueOf: valueOf };
16998 * @name $sceProvider
17001 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17002 * - enable/disable Strict Contextual Escaping (SCE) in a module
17003 * - override the default implementation with a custom delegate
17005 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17008 /* jshint maxlen: false*/
17017 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17019 * # Strict Contextual Escaping
17021 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17022 * contexts to result in a value that is marked as safe to use for that context. One example of
17023 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
17024 * to these contexts as privileged or SCE contexts.
17026 * As of version 1.2, Angular ships with SCE enabled by default.
17028 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
17029 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
17030 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17031 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17032 * to the top of your HTML document.
17034 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17035 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17037 * Here's an example of a binding in a privileged context:
17040 * <input ng-model="userHtml" aria-label="User input">
17041 * <div ng-bind-html="userHtml"></div>
17044 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
17045 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17046 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17047 * bindings. (HTML is just one example of a context where rendering user controlled input creates
17048 * security vulnerabilities.)
17050 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17051 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17053 * How would you ensure that every place that used these types of bindings was bound to a value that
17054 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
17055 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17056 * properties/fields and forgot to update the binding to the sanitized value?
17058 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17059 * determine that something explicitly says it's safe to use a value for binding in that
17060 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
17061 * for those values that you can easily tell are safe - because they were received from your server,
17062 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
17063 * allowing only the files in a specific directory to do this. Ensuring that the internal API
17064 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17066 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17067 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17068 * obtain values that will be accepted by SCE / privileged contexts.
17071 * ## How does it work?
17073 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17074 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
17075 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17076 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17078 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17079 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
17083 * var ngBindHtmlDirective = ['$sce', function($sce) {
17084 * return function(scope, element, attr) {
17085 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17086 * element.html(value || '');
17092 * ## Impact on loading templates
17094 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17095 * `templateUrl`'s specified by {@link guide/directive directives}.
17097 * By default, Angular only loads templates from the same domain and protocol as the application
17098 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
17099 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
17100 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17101 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17105 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17106 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17107 * policy apply in addition to this and may further restrict whether the template is successfully
17108 * loaded. This means that without the right CORS policy, loading templates from a different domain
17109 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
17112 * ## This feels like too much overhead
17114 * It's important to remember that SCE only applies to interpolation expressions.
17116 * If your expressions are constant literals, they're automatically trusted and you don't need to
17117 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17118 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17120 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17121 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
17123 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17124 * templates in `ng-include` from your application's domain without having to even know about SCE.
17125 * It blocks loading templates from other domains or loading templates over http from an https
17126 * served document. You can change these by setting your own custom {@link
17127 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17128 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17130 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
17131 * application that's secure and can be audited to verify that with much more ease than bolting
17132 * security onto an application later.
17134 * <a name="contexts"></a>
17135 * ## What trusted context types are supported?
17137 * | Context | Notes |
17138 * |---------------------|----------------|
17139 * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
17140 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
17141 * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
17142 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
17143 * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
17145 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17147 * Each element in these arrays must be one of the following:
17150 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
17151 * domain** as the application document using the **same protocol**.
17152 * - **String** (except the special value `'self'`)
17153 * - The string is matched against the full *normalized / absolute URL* of the resource
17154 * being tested (substring matches are not good enough.)
17155 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
17156 * match themselves.
17157 * - `*`: matches zero or more occurrences of any character other than one of the following 6
17158 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
17160 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
17161 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
17162 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17163 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
17164 * http://foo.example.com/templates/**).
17165 * - **RegExp** (*see caveat below*)
17166 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
17167 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
17168 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17169 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
17170 * small number of cases. A `.` character in the regex used when matching the scheme or a
17171 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
17172 * is highly recommended to use the string patterns and only fall back to regular expressions
17173 * as a last resort.
17174 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
17175 * matched against the **entire** *normalized / absolute URL* of the resource being tested
17176 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
17177 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17178 * - If you are generating your JavaScript from some other templating engine (not
17179 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17180 * remember to escape your regular expression (and be aware that you might need more than
17181 * one level of escaping depending on your templating engine and the way you interpolated
17182 * the value.) Do make use of your platform's escaping mechanism as it might be good
17183 * enough before coding your own. E.g. Ruby has
17184 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17185 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17186 * Javascript lacks a similar built in function for escaping. Take a look at Google
17187 * Closure library's [goog.string.regExpEscape(s)](
17188 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17190 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17192 * ## Show me an example using SCE.
17194 * <example module="mySceApp" deps="angular-sanitize.js">
17195 * <file name="index.html">
17196 * <div ng-controller="AppController as myCtrl">
17197 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17198 * <b>User comments</b><br>
17199 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17200 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
17202 * <div class="well">
17203 * <div ng-repeat="userComment in myCtrl.userComments">
17204 * <b>{{userComment.name}}</b>:
17205 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17212 * <file name="script.js">
17213 * angular.module('mySceApp', ['ngSanitize'])
17214 * .controller('AppController', ['$http', '$templateCache', '$sce',
17215 * function($http, $templateCache, $sce) {
17217 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17218 * self.userComments = userComments;
17220 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
17221 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17222 * 'sanitization."">Hover over this text.</span>');
17226 * <file name="test_data.json">
17228 * { "name": "Alice",
17230 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17233 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
17238 * <file name="protractor.js" type="protractor">
17239 * describe('SCE doc demo', function() {
17240 * it('should sanitize untrusted values', function() {
17241 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17242 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
17245 * it('should NOT sanitize explicitly trusted values', function() {
17246 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17247 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17248 * 'sanitization."">Hover over this text.</span>');
17256 * ## Can I disable SCE completely?
17258 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
17259 * for little coding overhead. It will be much harder to take an SCE disabled application and
17260 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
17261 * for cases where you have a lot of existing code that was written before SCE was introduced and
17262 * you're migrating them a module at a time.
17264 * That said, here's how you can completely disable SCE:
17267 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17268 * // Completely disable SCE. For demonstration purposes only!
17269 * // Do not use in new projects.
17270 * $sceProvider.enabled(false);
17275 /* jshint maxlen: 100 */
17277 function $SceProvider() {
17278 var enabled = true;
17282 * @name $sceProvider#enabled
17285 * @param {boolean=} value If provided, then enables/disables SCE.
17286 * @return {boolean} true if SCE is enabled, false otherwise.
17289 * Enables/disables SCE and returns the current value.
17291 this.enabled = function(value) {
17292 if (arguments.length) {
17299 /* Design notes on the default implementation for SCE.
17301 * The API contract for the SCE delegate
17302 * -------------------------------------
17303 * The SCE delegate object must provide the following 3 methods:
17305 * - trustAs(contextEnum, value)
17306 * This method is used to tell the SCE service that the provided value is OK to use in the
17307 * contexts specified by contextEnum. It must return an object that will be accepted by
17308 * getTrusted() for a compatible contextEnum and return this value.
17311 * For values that were not produced by trustAs(), return them as is. For values that were
17312 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
17313 * trustAs is wrapping the given values into some type, this operation unwraps it when given
17316 * - getTrusted(contextEnum, value)
17317 * This function should return the a value that is safe to use in the context specified by
17318 * contextEnum or throw and exception otherwise.
17320 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17321 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
17322 * instance, an implementation could maintain a registry of all trusted objects by context. In
17323 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
17324 * return the same object passed in if it was found in the registry under a compatible context or
17325 * throw an exception otherwise. An implementation might only wrap values some of the time based
17326 * on some criteria. getTrusted() might return a value and not throw an exception for special
17327 * constants or objects even if not wrapped. All such implementations fulfill this contract.
17330 * A note on the inheritance model for SCE contexts
17331 * ------------------------------------------------
17332 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
17333 * is purely an implementation details.
17335 * The contract is simply this:
17337 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17338 * will also succeed.
17340 * Inheritance happens to capture this in a natural way. In some future, we
17341 * may not use inheritance anymore. That is OK because no code outside of
17342 * sce.js and sceSpecs.js would need to be aware of this detail.
17345 this.$get = ['$parse', '$sceDelegate', function(
17346 $parse, $sceDelegate) {
17347 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
17348 // the "expression(javascript expression)" syntax which is insecure.
17349 if (enabled && msie < 8) {
17350 throw $sceMinErr('iequirks',
17351 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17352 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17353 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
17356 var sce = shallowCopy(SCE_CONTEXTS);
17360 * @name $sce#isEnabled
17363 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
17364 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17367 * Returns a boolean indicating if SCE is enabled.
17369 sce.isEnabled = function() {
17372 sce.trustAs = $sceDelegate.trustAs;
17373 sce.getTrusted = $sceDelegate.getTrusted;
17374 sce.valueOf = $sceDelegate.valueOf;
17377 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17378 sce.valueOf = identity;
17383 * @name $sce#parseAs
17386 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
17387 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
17388 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17391 * @param {string} type The kind of SCE context in which this result will be used.
17392 * @param {string} expression String expression to compile.
17393 * @returns {function(context, locals)} a function which represents the compiled expression:
17395 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17396 * are evaluated against (typically a scope object).
17397 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17400 sce.parseAs = function sceParseAs(type, expr) {
17401 var parsed = $parse(expr);
17402 if (parsed.literal && parsed.constant) {
17405 return $parse(expr, function(value) {
17406 return sce.getTrusted(type, value);
17413 * @name $sce#trustAs
17416 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
17417 * returns an object that is trusted by angular for use in specified strict contextual
17418 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17419 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
17420 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
17423 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
17424 * resourceUrl, html, js and css.
17425 * @param {*} value The value that that should be considered trusted/safe.
17426 * @returns {*} A value that can be used to stand in for the provided `value` in places
17427 * where Angular expects a $sce.trustAs() return value.
17432 * @name $sce#trustAsHtml
17435 * Shorthand method. `$sce.trustAsHtml(value)` →
17436 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17438 * @param {*} value The value to trustAs.
17439 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17440 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
17441 * only accept expressions that are either literal constants or are the
17442 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17447 * @name $sce#trustAsUrl
17450 * Shorthand method. `$sce.trustAsUrl(value)` →
17451 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17453 * @param {*} value The value to trustAs.
17454 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17455 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
17456 * only accept expressions that are either literal constants or are the
17457 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17462 * @name $sce#trustAsResourceUrl
17465 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
17466 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17468 * @param {*} value The value to trustAs.
17469 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17470 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
17471 * only accept expressions that are either literal constants or are the return
17472 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
17477 * @name $sce#trustAsJs
17480 * Shorthand method. `$sce.trustAsJs(value)` →
17481 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17483 * @param {*} value The value to trustAs.
17484 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17485 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
17486 * only accept expressions that are either literal constants or are the
17487 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17492 * @name $sce#getTrusted
17495 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
17496 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17497 * originally supplied value if the queried context type is a supertype of the created type.
17498 * If this condition isn't satisfied, throws an exception.
17500 * @param {string} type The kind of context in which this value is to be used.
17501 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17503 * @returns {*} The value the was originally provided to
17504 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17505 * Otherwise, throws an exception.
17510 * @name $sce#getTrustedHtml
17513 * Shorthand method. `$sce.getTrustedHtml(value)` →
17514 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17516 * @param {*} value The value to pass to `$sce.getTrusted`.
17517 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17522 * @name $sce#getTrustedCss
17525 * Shorthand method. `$sce.getTrustedCss(value)` →
17526 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17528 * @param {*} value The value to pass to `$sce.getTrusted`.
17529 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17534 * @name $sce#getTrustedUrl
17537 * Shorthand method. `$sce.getTrustedUrl(value)` →
17538 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17540 * @param {*} value The value to pass to `$sce.getTrusted`.
17541 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17546 * @name $sce#getTrustedResourceUrl
17549 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
17550 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17552 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17553 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17558 * @name $sce#getTrustedJs
17561 * Shorthand method. `$sce.getTrustedJs(value)` →
17562 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17564 * @param {*} value The value to pass to `$sce.getTrusted`.
17565 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17570 * @name $sce#parseAsHtml
17573 * Shorthand method. `$sce.parseAsHtml(expression string)` →
17574 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17576 * @param {string} expression String expression to compile.
17577 * @returns {function(context, locals)} a function which represents the compiled expression:
17579 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17580 * are evaluated against (typically a scope object).
17581 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17587 * @name $sce#parseAsCss
17590 * Shorthand method. `$sce.parseAsCss(value)` →
17591 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17593 * @param {string} expression String expression to compile.
17594 * @returns {function(context, locals)} a function which represents the compiled expression:
17596 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17597 * are evaluated against (typically a scope object).
17598 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17604 * @name $sce#parseAsUrl
17607 * Shorthand method. `$sce.parseAsUrl(value)` →
17608 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17610 * @param {string} expression String expression to compile.
17611 * @returns {function(context, locals)} a function which represents the compiled expression:
17613 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17614 * are evaluated against (typically a scope object).
17615 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17621 * @name $sce#parseAsResourceUrl
17624 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
17625 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17627 * @param {string} expression String expression to compile.
17628 * @returns {function(context, locals)} a function which represents the compiled expression:
17630 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17631 * are evaluated against (typically a scope object).
17632 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17638 * @name $sce#parseAsJs
17641 * Shorthand method. `$sce.parseAsJs(value)` →
17642 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17644 * @param {string} expression String expression to compile.
17645 * @returns {function(context, locals)} a function which represents the compiled expression:
17647 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17648 * are evaluated against (typically a scope object).
17649 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17653 // Shorthand delegations.
17654 var parse = sce.parseAs,
17655 getTrusted = sce.getTrusted,
17656 trustAs = sce.trustAs;
17658 forEach(SCE_CONTEXTS, function(enumValue, name) {
17659 var lName = lowercase(name);
17660 sce[camelCase("parse_as_" + lName)] = function(expr) {
17661 return parse(enumValue, expr);
17663 sce[camelCase("get_trusted_" + lName)] = function(value) {
17664 return getTrusted(enumValue, value);
17666 sce[camelCase("trust_as_" + lName)] = function(value) {
17667 return trustAs(enumValue, value);
17676 * !!! This is an undocumented "private" service !!!
17679 * @requires $window
17680 * @requires $document
17682 * @property {boolean} history Does the browser support html5 history api ?
17683 * @property {boolean} transitions Does the browser support CSS transition events ?
17684 * @property {boolean} animations Does the browser support CSS animation events ?
17687 * This is very simple implementation of testing browser's features.
17689 function $SnifferProvider() {
17690 this.$get = ['$window', '$document', function($window, $document) {
17691 var eventSupport = {},
17693 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17694 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17695 document = $document[0] || {},
17697 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17698 bodyStyle = document.body && document.body.style,
17699 transitions = false,
17700 animations = false,
17704 for (var prop in bodyStyle) {
17705 if (match = vendorRegex.exec(prop)) {
17706 vendorPrefix = match[0];
17707 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17712 if (!vendorPrefix) {
17713 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17716 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17717 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17719 if (android && (!transitions || !animations)) {
17720 transitions = isString(bodyStyle.webkitTransition);
17721 animations = isString(bodyStyle.webkitAnimation);
17727 // Android has history.pushState, but it does not update location correctly
17728 // so let's not use the history API at all.
17729 // http://code.google.com/p/android/issues/detail?id=17471
17730 // https://github.com/angular/angular.js/issues/904
17732 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17733 // so let's not use the history API also
17734 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17736 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17738 hasEvent: function(event) {
17739 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17740 // it. In particular the event is not fired when backspace or delete key are pressed or
17741 // when cut operation is performed.
17742 // IE10+ implements 'input' event but it erroneously fires under various situations,
17743 // e.g. when placeholder changes, or a form is focused.
17744 if (event === 'input' && msie <= 11) return false;
17746 if (isUndefined(eventSupport[event])) {
17747 var divElm = document.createElement('div');
17748 eventSupport[event] = 'on' + event in divElm;
17751 return eventSupport[event];
17754 vendorPrefix: vendorPrefix,
17755 transitions: transitions,
17756 animations: animations,
17762 var $compileMinErr = minErr('$compile');
17766 * @name $templateRequest
17769 * The `$templateRequest` service runs security checks then downloads the provided template using
17770 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17771 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17772 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17773 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17774 * when `tpl` is of type string and `$templateCache` has the matching entry.
17776 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17777 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17779 * @return {Promise} a promise for the HTTP response data of the given URL.
17781 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17783 function $TemplateRequestProvider() {
17784 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17785 function handleRequestFn(tpl, ignoreRequestError) {
17786 handleRequestFn.totalPendingRequests++;
17788 // We consider the template cache holds only trusted templates, so
17789 // there's no need to go through whitelisting again for keys that already
17790 // are included in there. This also makes Angular accept any script
17791 // directive, no matter its name. However, we still need to unwrap trusted
17793 if (!isString(tpl) || !$templateCache.get(tpl)) {
17794 tpl = $sce.getTrustedResourceUrl(tpl);
17797 var transformResponse = $http.defaults && $http.defaults.transformResponse;
17799 if (isArray(transformResponse)) {
17800 transformResponse = transformResponse.filter(function(transformer) {
17801 return transformer !== defaultHttpResponseTransform;
17803 } else if (transformResponse === defaultHttpResponseTransform) {
17804 transformResponse = null;
17807 var httpOptions = {
17808 cache: $templateCache,
17809 transformResponse: transformResponse
17812 return $http.get(tpl, httpOptions)
17813 ['finally'](function() {
17814 handleRequestFn.totalPendingRequests--;
17816 .then(function(response) {
17817 $templateCache.put(tpl, response.data);
17818 return response.data;
17821 function handleError(resp) {
17822 if (!ignoreRequestError) {
17823 throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17824 tpl, resp.status, resp.statusText);
17826 return $q.reject(resp);
17830 handleRequestFn.totalPendingRequests = 0;
17832 return handleRequestFn;
17836 function $$TestabilityProvider() {
17837 this.$get = ['$rootScope', '$browser', '$location',
17838 function($rootScope, $browser, $location) {
17841 * @name $testability
17844 * The private $$testability service provides a collection of methods for use when debugging
17845 * or by automated test and debugging tools.
17847 var testability = {};
17850 * @name $$testability#findBindings
17853 * Returns an array of elements that are bound (via ng-bind or {{}})
17854 * to expressions matching the input.
17856 * @param {Element} element The element root to search from.
17857 * @param {string} expression The binding expression to match.
17858 * @param {boolean} opt_exactMatch If true, only returns exact matches
17859 * for the expression. Filters and whitespace are ignored.
17861 testability.findBindings = function(element, expression, opt_exactMatch) {
17862 var bindings = element.getElementsByClassName('ng-binding');
17864 forEach(bindings, function(binding) {
17865 var dataBinding = angular.element(binding).data('$binding');
17867 forEach(dataBinding, function(bindingName) {
17868 if (opt_exactMatch) {
17869 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17870 if (matcher.test(bindingName)) {
17871 matches.push(binding);
17874 if (bindingName.indexOf(expression) != -1) {
17875 matches.push(binding);
17885 * @name $$testability#findModels
17888 * Returns an array of elements that are two-way found via ng-model to
17889 * expressions matching the input.
17891 * @param {Element} element The element root to search from.
17892 * @param {string} expression The model expression to match.
17893 * @param {boolean} opt_exactMatch If true, only returns exact matches
17894 * for the expression.
17896 testability.findModels = function(element, expression, opt_exactMatch) {
17897 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17898 for (var p = 0; p < prefixes.length; ++p) {
17899 var attributeEquals = opt_exactMatch ? '=' : '*=';
17900 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17901 var elements = element.querySelectorAll(selector);
17902 if (elements.length) {
17909 * @name $$testability#getLocation
17912 * Shortcut for getting the location in a browser agnostic way. Returns
17913 * the path, search, and hash. (e.g. /path?a=b#hash)
17915 testability.getLocation = function() {
17916 return $location.url();
17920 * @name $$testability#setLocation
17923 * Shortcut for navigating to a location without doing a full page reload.
17925 * @param {string} url The location url (path, search and hash,
17926 * e.g. /path?a=b#hash) to go to.
17928 testability.setLocation = function(url) {
17929 if (url !== $location.url()) {
17930 $location.url(url);
17931 $rootScope.$digest();
17936 * @name $$testability#whenStable
17939 * Calls the callback when $timeout and $http requests are completed.
17941 * @param {function} callback
17943 testability.whenStable = function(callback) {
17944 $browser.notifyWhenNoOutstandingRequests(callback);
17947 return testability;
17951 function $TimeoutProvider() {
17952 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17953 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
17955 var deferreds = {};
17963 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17964 * block and delegates any exceptions to
17965 * {@link ng.$exceptionHandler $exceptionHandler} service.
17967 * The return value of calling `$timeout` is a promise, which will be resolved when
17968 * the delay has passed and the timeout function, if provided, is executed.
17970 * To cancel a timeout request, call `$timeout.cancel(promise)`.
17972 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17973 * synchronously flush the queue of deferred functions.
17975 * If you only want a promise that will be resolved after some specified delay
17976 * then you can call `$timeout` without the `fn` function.
17978 * @param {function()=} fn A function, whose execution should be delayed.
17979 * @param {number=} [delay=0] Delay in milliseconds.
17980 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17981 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17982 * @param {...*=} Pass additional parameters to the executed function.
17983 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17984 * promise will be resolved with is the return value of the `fn` function.
17987 function timeout(fn, delay, invokeApply) {
17988 if (!isFunction(fn)) {
17989 invokeApply = delay;
17994 var args = sliceArgs(arguments, 3),
17995 skipApply = (isDefined(invokeApply) && !invokeApply),
17996 deferred = (skipApply ? $$q : $q).defer(),
17997 promise = deferred.promise,
18000 timeoutId = $browser.defer(function() {
18002 deferred.resolve(fn.apply(null, args));
18004 deferred.reject(e);
18005 $exceptionHandler(e);
18008 delete deferreds[promise.$$timeoutId];
18011 if (!skipApply) $rootScope.$apply();
18014 promise.$$timeoutId = timeoutId;
18015 deferreds[timeoutId] = deferred;
18023 * @name $timeout#cancel
18026 * Cancels a task associated with the `promise`. As a result of this, the promise will be
18027 * resolved with a rejection.
18029 * @param {Promise=} promise Promise returned by the `$timeout` function.
18030 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18033 timeout.cancel = function(promise) {
18034 if (promise && promise.$$timeoutId in deferreds) {
18035 deferreds[promise.$$timeoutId].reject('canceled');
18036 delete deferreds[promise.$$timeoutId];
18037 return $browser.defer.cancel(promise.$$timeoutId);
18046 // NOTE: The usage of window and document instead of $window and $document here is
18047 // deliberate. This service depends on the specific behavior of anchor nodes created by the
18048 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18049 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
18050 // doesn't know about mocked locations and resolves URLs to the real document - which is
18051 // exactly the behavior needed here. There is little value is mocking these out for this
18053 var urlParsingNode = document.createElement("a");
18054 var originUrl = urlResolve(window.location.href);
18059 * Implementation Notes for non-IE browsers
18060 * ----------------------------------------
18061 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18062 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
18063 * URL will be resolved into an absolute URL in the context of the application document.
18064 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18065 * properties are all populated to reflect the normalized URL. This approach has wide
18066 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
18067 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18069 * Implementation Notes for IE
18070 * ---------------------------
18071 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18072 * browsers. However, the parsed components will not be set if the URL assigned did not specify
18073 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
18074 * work around that by performing the parsing in a 2nd step by taking a previously normalized
18075 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
18076 * properties such as protocol, hostname, port, etc.
18079 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18080 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18081 * http://url.spec.whatwg.org/#urlutils
18082 * https://github.com/angular/angular.js/pull/2902
18083 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18086 * @param {string} url The URL to be parsed.
18087 * @description Normalizes and parses a URL.
18088 * @returns {object} Returns the normalized URL as a dictionary.
18090 * | member name | Description |
18091 * |---------------|----------------|
18092 * | href | A normalized version of the provided URL if it was not an absolute URL |
18093 * | protocol | The protocol including the trailing colon |
18094 * | host | The host and port (if the port is non-default) of the normalizedUrl |
18095 * | search | The search params, minus the question mark |
18096 * | hash | The hash string, minus the hash symbol
18097 * | hostname | The hostname
18098 * | port | The port, without ":"
18099 * | pathname | The pathname, beginning with "/"
18102 function urlResolve(url) {
18106 // Normalize before parse. Refer Implementation Notes on why this is
18107 // done in two steps on IE.
18108 urlParsingNode.setAttribute("href", href);
18109 href = urlParsingNode.href;
18112 urlParsingNode.setAttribute('href', href);
18114 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18116 href: urlParsingNode.href,
18117 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18118 host: urlParsingNode.host,
18119 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18120 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18121 hostname: urlParsingNode.hostname,
18122 port: urlParsingNode.port,
18123 pathname: (urlParsingNode.pathname.charAt(0) === '/')
18124 ? urlParsingNode.pathname
18125 : '/' + urlParsingNode.pathname
18130 * Parse a request URL and determine whether this is a same-origin request as the application document.
18132 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18133 * or a parsed URL object.
18134 * @returns {boolean} Whether the request is for the same origin as the application document.
18136 function urlIsSameOrigin(requestUrl) {
18137 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18138 return (parsed.protocol === originUrl.protocol &&
18139 parsed.host === originUrl.host);
18147 * A reference to the browser's `window` object. While `window`
18148 * is globally available in JavaScript, it causes testability problems, because
18149 * it is a global variable. In angular we always refer to it through the
18150 * `$window` service, so it may be overridden, removed or mocked for testing.
18152 * Expressions, like the one defined for the `ngClick` directive in the example
18153 * below, are evaluated with respect to the current scope. Therefore, there is
18154 * no risk of inadvertently coding in a dependency on a global value in such an
18158 <example module="windowExample">
18159 <file name="index.html">
18161 angular.module('windowExample', [])
18162 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18163 $scope.greeting = 'Hello, World!';
18164 $scope.doGreeting = function(greeting) {
18165 $window.alert(greeting);
18169 <div ng-controller="ExampleController">
18170 <input type="text" ng-model="greeting" aria-label="greeting" />
18171 <button ng-click="doGreeting(greeting)">ALERT</button>
18174 <file name="protractor.js" type="protractor">
18175 it('should display the greeting in the input box', function() {
18176 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18177 // If we click the button it will block the test runner
18178 // element(':button').click();
18183 function $WindowProvider() {
18184 this.$get = valueFn(window);
18188 * @name $$cookieReader
18189 * @requires $document
18192 * This is a private service for reading cookies used by $http and ngCookies
18194 * @return {Object} a key/value map of the current cookies
18196 function $$CookieReader($document) {
18197 var rawDocument = $document[0] || {};
18198 var lastCookies = {};
18199 var lastCookieString = '';
18201 function safeDecodeURIComponent(str) {
18203 return decodeURIComponent(str);
18209 return function() {
18210 var cookieArray, cookie, i, index, name;
18211 var currentCookieString = rawDocument.cookie || '';
18213 if (currentCookieString !== lastCookieString) {
18214 lastCookieString = currentCookieString;
18215 cookieArray = lastCookieString.split('; ');
18218 for (i = 0; i < cookieArray.length; i++) {
18219 cookie = cookieArray[i];
18220 index = cookie.indexOf('=');
18221 if (index > 0) { //ignore nameless cookies
18222 name = safeDecodeURIComponent(cookie.substring(0, index));
18223 // the first value that is seen for a cookie is the most
18224 // specific one. values for the same cookie name that
18225 // follow are for less specific paths.
18226 if (isUndefined(lastCookies[name])) {
18227 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18232 return lastCookies;
18236 $$CookieReader.$inject = ['$document'];
18238 function $$CookieReaderProvider() {
18239 this.$get = $$CookieReader;
18242 /* global currencyFilter: true,
18244 filterFilter: true,
18246 limitToFilter: true,
18247 lowercaseFilter: true,
18248 numberFilter: true,
18249 orderByFilter: true,
18250 uppercaseFilter: true,
18255 * @name $filterProvider
18258 * Filters are just functions which transform input to an output. However filters need to be
18259 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18260 * annotated with dependencies and is responsible for creating a filter function.
18262 * <div class="alert alert-warning">
18263 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18264 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18265 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18266 * (`myapp_subsection_filterx`).
18270 * // Filter registration
18271 * function MyModule($provide, $filterProvider) {
18272 * // create a service to demonstrate injection (not always needed)
18273 * $provide.value('greet', function(name){
18274 * return 'Hello ' + name + '!';
18277 * // register a filter factory which uses the
18278 * // greet service to demonstrate DI.
18279 * $filterProvider.register('greet', function(greet){
18280 * // return the filter function which uses the greet service
18281 * // to generate salutation
18282 * return function(text) {
18283 * // filters need to be forgiving so check input validity
18284 * return text && greet(text) || text;
18290 * The filter function is registered with the `$injector` under the filter name suffix with
18294 * it('should be the same instance', inject(
18295 * function($filterProvider) {
18296 * $filterProvider.register('reverse', function(){
18300 * function($filter, reverseFilter) {
18301 * expect($filter('reverse')).toBe(reverseFilter);
18306 * For more information about how angular filters work, and how to create your own filters, see
18307 * {@link guide/filter Filters} in the Angular Developer Guide.
18315 * Filters are used for formatting data displayed to the user.
18317 * The general syntax in templates is as follows:
18319 * {{ expression [| filter_name[:parameter_value] ... ] }}
18321 * @param {String} name Name of the filter function to retrieve
18322 * @return {Function} the filter function
18324 <example name="$filter" module="filterExample">
18325 <file name="index.html">
18326 <div ng-controller="MainCtrl">
18327 <h3>{{ originalText }}</h3>
18328 <h3>{{ filteredText }}</h3>
18332 <file name="script.js">
18333 angular.module('filterExample', [])
18334 .controller('MainCtrl', function($scope, $filter) {
18335 $scope.originalText = 'hello';
18336 $scope.filteredText = $filter('uppercase')($scope.originalText);
18341 $FilterProvider.$inject = ['$provide'];
18342 function $FilterProvider($provide) {
18343 var suffix = 'Filter';
18347 * @name $filterProvider#register
18348 * @param {string|Object} name Name of the filter function, or an object map of filters where
18349 * the keys are the filter names and the values are the filter factories.
18351 * <div class="alert alert-warning">
18352 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18353 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18354 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18355 * (`myapp_subsection_filterx`).
18357 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18358 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18359 * of the registered filter instances.
18361 function register(name, factory) {
18362 if (isObject(name)) {
18364 forEach(name, function(filter, key) {
18365 filters[key] = register(key, filter);
18369 return $provide.factory(name + suffix, factory);
18372 this.register = register;
18374 this.$get = ['$injector', function($injector) {
18375 return function(name) {
18376 return $injector.get(name + suffix);
18380 ////////////////////////////////////////
18383 currencyFilter: false,
18385 filterFilter: false,
18387 limitToFilter: false,
18388 lowercaseFilter: false,
18389 numberFilter: false,
18390 orderByFilter: false,
18391 uppercaseFilter: false,
18394 register('currency', currencyFilter);
18395 register('date', dateFilter);
18396 register('filter', filterFilter);
18397 register('json', jsonFilter);
18398 register('limitTo', limitToFilter);
18399 register('lowercase', lowercaseFilter);
18400 register('number', numberFilter);
18401 register('orderBy', orderByFilter);
18402 register('uppercase', uppercaseFilter);
18411 * Selects a subset of items from `array` and returns it as a new array.
18413 * @param {Array} array The source array.
18414 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18419 * - `string`: The string is used for matching against the contents of the `array`. All strings or
18420 * objects with string properties in `array` that match this string will be returned. This also
18421 * applies to nested object properties.
18422 * The predicate can be negated by prefixing the string with `!`.
18424 * - `Object`: A pattern object can be used to filter specific properties on objects contained
18425 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18426 * which have property `name` containing "M" and property `phone` containing "1". A special
18427 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18428 * property of the object or its nested object properties. That's equivalent to the simple
18429 * substring match with a `string` as described above. The predicate can be negated by prefixing
18430 * the string with `!`.
18431 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
18432 * not containing "M".
18434 * Note that a named property will match properties on the same level only, while the special
18435 * `$` property will match properties on the same level or deeper. E.g. an array item like
18436 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18437 * **will** be matched by `{$: 'John'}`.
18439 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18440 * The function is called for each element of the array, with the element, its index, and
18441 * the entire array itself as arguments.
18443 * The final result is an array of those elements that the predicate returned true for.
18445 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18446 * determining if the expected value (from the filter expression) and actual value (from
18447 * the object in the array) should be considered a match.
18451 * - `function(actual, expected)`:
18452 * The function will be given the object value and the predicate value to compare and
18453 * should return true if both values should be considered equal.
18455 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18456 * This is essentially strict comparison of expected and actual.
18458 * - `false|undefined`: A short hand for a function which will look for a substring match in case
18461 * Primitive values are converted to strings. Objects are not compared against primitives,
18462 * unless they have a custom `toString` method (e.g. `Date` objects).
18466 <file name="index.html">
18467 <div ng-init="friends = [{name:'John', phone:'555-1276'},
18468 {name:'Mary', phone:'800-BIG-MARY'},
18469 {name:'Mike', phone:'555-4321'},
18470 {name:'Adam', phone:'555-5678'},
18471 {name:'Julie', phone:'555-8765'},
18472 {name:'Juliette', phone:'555-5678'}]"></div>
18474 <label>Search: <input ng-model="searchText"></label>
18475 <table id="searchTextResults">
18476 <tr><th>Name</th><th>Phone</th></tr>
18477 <tr ng-repeat="friend in friends | filter:searchText">
18478 <td>{{friend.name}}</td>
18479 <td>{{friend.phone}}</td>
18483 <label>Any: <input ng-model="search.$"></label> <br>
18484 <label>Name only <input ng-model="search.name"></label><br>
18485 <label>Phone only <input ng-model="search.phone"></label><br>
18486 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18487 <table id="searchObjResults">
18488 <tr><th>Name</th><th>Phone</th></tr>
18489 <tr ng-repeat="friendObj in friends | filter:search:strict">
18490 <td>{{friendObj.name}}</td>
18491 <td>{{friendObj.phone}}</td>
18495 <file name="protractor.js" type="protractor">
18496 var expectFriendNames = function(expectedNames, key) {
18497 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18498 arr.forEach(function(wd, i) {
18499 expect(wd.getText()).toMatch(expectedNames[i]);
18504 it('should search across all fields when filtering with a string', function() {
18505 var searchText = element(by.model('searchText'));
18506 searchText.clear();
18507 searchText.sendKeys('m');
18508 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18510 searchText.clear();
18511 searchText.sendKeys('76');
18512 expectFriendNames(['John', 'Julie'], 'friend');
18515 it('should search in specific fields when filtering with a predicate object', function() {
18516 var searchAny = element(by.model('search.$'));
18518 searchAny.sendKeys('i');
18519 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18521 it('should use a equal comparison when comparator is true', function() {
18522 var searchName = element(by.model('search.name'));
18523 var strict = element(by.model('strict'));
18524 searchName.clear();
18525 searchName.sendKeys('Julie');
18527 expectFriendNames(['Julie'], 'friendObj');
18532 function filterFilter() {
18533 return function(array, expression, comparator) {
18534 if (!isArrayLike(array)) {
18535 if (array == null) {
18538 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18542 var expressionType = getTypeForFilter(expression);
18544 var matchAgainstAnyProp;
18546 switch (expressionType) {
18548 predicateFn = expression;
18554 matchAgainstAnyProp = true;
18558 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18564 return Array.prototype.filter.call(array, predicateFn);
18568 // Helper functions for `filterFilter`
18569 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18570 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18573 if (comparator === true) {
18574 comparator = equals;
18575 } else if (!isFunction(comparator)) {
18576 comparator = function(actual, expected) {
18577 if (isUndefined(actual)) {
18578 // No substring matching against `undefined`
18581 if ((actual === null) || (expected === null)) {
18582 // No substring matching against `null`; only match against `null`
18583 return actual === expected;
18585 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18586 // Should not compare primitives against objects, unless they have custom `toString` method
18590 actual = lowercase('' + actual);
18591 expected = lowercase('' + expected);
18592 return actual.indexOf(expected) !== -1;
18596 predicateFn = function(item) {
18597 if (shouldMatchPrimitives && !isObject(item)) {
18598 return deepCompare(item, expression.$, comparator, false);
18600 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18603 return predicateFn;
18606 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18607 var actualType = getTypeForFilter(actual);
18608 var expectedType = getTypeForFilter(expected);
18610 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18611 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18612 } else if (isArray(actual)) {
18613 // In case `actual` is an array, consider it a match
18614 // if ANY of it's items matches `expected`
18615 return actual.some(function(item) {
18616 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18620 switch (actualType) {
18623 if (matchAgainstAnyProp) {
18624 for (key in actual) {
18625 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18629 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18630 } else if (expectedType === 'object') {
18631 for (key in expected) {
18632 var expectedVal = expected[key];
18633 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18637 var matchAnyProperty = key === '$';
18638 var actualVal = matchAnyProperty ? actual : actual[key];
18639 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18645 return comparator(actual, expected);
18651 return comparator(actual, expected);
18655 // Used for easily differentiating between `null` and actual `object`
18656 function getTypeForFilter(val) {
18657 return (val === null) ? 'null' : typeof val;
18666 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18667 * symbol for current locale is used.
18669 * @param {number} amount Input to filter.
18670 * @param {string=} symbol Currency symbol or identifier to be displayed.
18671 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18672 * @returns {string} Formatted number.
18676 <example module="currencyExample">
18677 <file name="index.html">
18679 angular.module('currencyExample', [])
18680 .controller('ExampleController', ['$scope', function($scope) {
18681 $scope.amount = 1234.56;
18684 <div ng-controller="ExampleController">
18685 <input type="number" ng-model="amount" aria-label="amount"> <br>
18686 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18687 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18688 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18691 <file name="protractor.js" type="protractor">
18692 it('should init with 1234.56', function() {
18693 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18694 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18695 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18697 it('should update', function() {
18698 if (browser.params.browser == 'safari') {
18699 // Safari does not understand the minus key. See
18700 // https://github.com/angular/protractor/issues/481
18703 element(by.model('amount')).clear();
18704 element(by.model('amount')).sendKeys('-1234');
18705 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18706 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18707 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18712 currencyFilter.$inject = ['$locale'];
18713 function currencyFilter($locale) {
18714 var formats = $locale.NUMBER_FORMATS;
18715 return function(amount, currencySymbol, fractionSize) {
18716 if (isUndefined(currencySymbol)) {
18717 currencySymbol = formats.CURRENCY_SYM;
18720 if (isUndefined(fractionSize)) {
18721 fractionSize = formats.PATTERNS[1].maxFrac;
18724 // if null or undefined pass it through
18725 return (amount == null)
18727 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18728 replace(/\u00A4/g, currencySymbol);
18738 * Formats a number as text.
18740 * If the input is null or undefined, it will just be returned.
18741 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18742 * If the input is not a number an empty string is returned.
18745 * @param {number|string} number Number to format.
18746 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18747 * If this is not provided then the fraction size is computed from the current locale's number
18748 * formatting pattern. In the case of the default locale, it will be 3.
18749 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18752 <example module="numberFilterExample">
18753 <file name="index.html">
18755 angular.module('numberFilterExample', [])
18756 .controller('ExampleController', ['$scope', function($scope) {
18757 $scope.val = 1234.56789;
18760 <div ng-controller="ExampleController">
18761 <label>Enter number: <input ng-model='val'></label><br>
18762 Default formatting: <span id='number-default'>{{val | number}}</span><br>
18763 No fractions: <span>{{val | number:0}}</span><br>
18764 Negative number: <span>{{-val | number:4}}</span>
18767 <file name="protractor.js" type="protractor">
18768 it('should format numbers', function() {
18769 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18770 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18771 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18774 it('should update', function() {
18775 element(by.model('val')).clear();
18776 element(by.model('val')).sendKeys('3374.333');
18777 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18778 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18779 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18786 numberFilter.$inject = ['$locale'];
18787 function numberFilter($locale) {
18788 var formats = $locale.NUMBER_FORMATS;
18789 return function(number, fractionSize) {
18791 // if null or undefined pass it through
18792 return (number == null)
18794 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18799 var DECIMAL_SEP = '.';
18800 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18801 if (isObject(number)) return '';
18803 var isNegative = number < 0;
18804 number = Math.abs(number);
18806 var isInfinity = number === Infinity;
18807 if (!isInfinity && !isFinite(number)) return '';
18809 var numStr = number + '',
18811 hasExponent = false,
18814 if (isInfinity) formatedText = '\u221e';
18816 if (!isInfinity && numStr.indexOf('e') !== -1) {
18817 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18818 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18821 formatedText = numStr;
18822 hasExponent = true;
18826 if (!isInfinity && !hasExponent) {
18827 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18829 // determine fractionSize if it is not specified
18830 if (isUndefined(fractionSize)) {
18831 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18834 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18836 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18837 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18839 var fraction = ('' + number).split(DECIMAL_SEP);
18840 var whole = fraction[0];
18841 fraction = fraction[1] || '';
18844 lgroup = pattern.lgSize,
18845 group = pattern.gSize;
18847 if (whole.length >= (lgroup + group)) {
18848 pos = whole.length - lgroup;
18849 for (i = 0; i < pos; i++) {
18850 if ((pos - i) % group === 0 && i !== 0) {
18851 formatedText += groupSep;
18853 formatedText += whole.charAt(i);
18857 for (i = pos; i < whole.length; i++) {
18858 if ((whole.length - i) % lgroup === 0 && i !== 0) {
18859 formatedText += groupSep;
18861 formatedText += whole.charAt(i);
18864 // format fraction part.
18865 while (fraction.length < fractionSize) {
18869 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18871 if (fractionSize > 0 && number < 1) {
18872 formatedText = number.toFixed(fractionSize);
18873 number = parseFloat(formatedText);
18874 formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18878 if (number === 0) {
18879 isNegative = false;
18882 parts.push(isNegative ? pattern.negPre : pattern.posPre,
18884 isNegative ? pattern.negSuf : pattern.posSuf);
18885 return parts.join('');
18888 function padNumber(num, digits, trim) {
18895 while (num.length < digits) num = '0' + num;
18897 num = num.substr(num.length - digits);
18903 function dateGetter(name, size, offset, trim) {
18904 offset = offset || 0;
18905 return function(date) {
18906 var value = date['get' + name]();
18907 if (offset > 0 || value > -offset) {
18910 if (value === 0 && offset == -12) value = 12;
18911 return padNumber(value, size, trim);
18915 function dateStrGetter(name, shortForm) {
18916 return function(date, formats) {
18917 var value = date['get' + name]();
18918 var get = uppercase(shortForm ? ('SHORT' + name) : name);
18920 return formats[get][value];
18924 function timeZoneGetter(date, formats, offset) {
18925 var zone = -1 * offset;
18926 var paddedZone = (zone >= 0) ? "+" : "";
18928 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18929 padNumber(Math.abs(zone % 60), 2);
18934 function getFirstThursdayOfYear(year) {
18935 // 0 = index of January
18936 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18937 // 4 = index of Thursday (+1 to account for 1st = 5)
18938 // 11 = index of *next* Thursday (+1 account for 1st = 12)
18939 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18942 function getThursdayThisWeek(datetime) {
18943 return new Date(datetime.getFullYear(), datetime.getMonth(),
18944 // 4 = index of Thursday
18945 datetime.getDate() + (4 - datetime.getDay()));
18948 function weekGetter(size) {
18949 return function(date) {
18950 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18951 thisThurs = getThursdayThisWeek(date);
18953 var diff = +thisThurs - +firstThurs,
18954 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18956 return padNumber(result, size);
18960 function ampmGetter(date, formats) {
18961 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18964 function eraGetter(date, formats) {
18965 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18968 function longEraGetter(date, formats) {
18969 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18972 var DATE_FORMATS = {
18973 yyyy: dateGetter('FullYear', 4),
18974 yy: dateGetter('FullYear', 2, 0, true),
18975 y: dateGetter('FullYear', 1),
18976 MMMM: dateStrGetter('Month'),
18977 MMM: dateStrGetter('Month', true),
18978 MM: dateGetter('Month', 2, 1),
18979 M: dateGetter('Month', 1, 1),
18980 dd: dateGetter('Date', 2),
18981 d: dateGetter('Date', 1),
18982 HH: dateGetter('Hours', 2),
18983 H: dateGetter('Hours', 1),
18984 hh: dateGetter('Hours', 2, -12),
18985 h: dateGetter('Hours', 1, -12),
18986 mm: dateGetter('Minutes', 2),
18987 m: dateGetter('Minutes', 1),
18988 ss: dateGetter('Seconds', 2),
18989 s: dateGetter('Seconds', 1),
18990 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18991 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18992 sss: dateGetter('Milliseconds', 3),
18993 EEEE: dateStrGetter('Day'),
18994 EEE: dateStrGetter('Day', true),
19002 GGGG: longEraGetter
19005 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19006 NUMBER_STRING = /^\-?\d+$/;
19014 * Formats `date` to a string based on the requested `format`.
19016 * `format` string can be composed of the following elements:
19018 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19019 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19020 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19021 * * `'MMMM'`: Month in year (January-December)
19022 * * `'MMM'`: Month in year (Jan-Dec)
19023 * * `'MM'`: Month in year, padded (01-12)
19024 * * `'M'`: Month in year (1-12)
19025 * * `'dd'`: Day in month, padded (01-31)
19026 * * `'d'`: Day in month (1-31)
19027 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
19028 * * `'EEE'`: Day in Week, (Sun-Sat)
19029 * * `'HH'`: Hour in day, padded (00-23)
19030 * * `'H'`: Hour in day (0-23)
19031 * * `'hh'`: Hour in AM/PM, padded (01-12)
19032 * * `'h'`: Hour in AM/PM, (1-12)
19033 * * `'mm'`: Minute in hour, padded (00-59)
19034 * * `'m'`: Minute in hour (0-59)
19035 * * `'ss'`: Second in minute, padded (00-59)
19036 * * `'s'`: Second in minute (0-59)
19037 * * `'sss'`: Millisecond in second, padded (000-999)
19038 * * `'a'`: AM/PM marker
19039 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19040 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19041 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19042 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19043 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19045 * `format` string can also be one of the following predefined
19046 * {@link guide/i18n localizable formats}:
19048 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19049 * (e.g. Sep 3, 2010 12:05:08 PM)
19050 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
19051 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
19052 * (e.g. Friday, September 3, 2010)
19053 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
19054 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
19055 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19056 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19057 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19059 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19060 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19061 * (e.g. `"h 'o''clock'"`).
19063 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19064 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19065 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19066 * specified in the string input, the time is considered to be in the local timezone.
19067 * @param {string=} format Formatting rules (see Description). If not specified,
19068 * `mediumDate` is used.
19069 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19070 * continental US time zone abbreviations, but for general use, use a time zone offset, for
19071 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19072 * If not specified, the timezone of the browser will be used.
19073 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19077 <file name="index.html">
19078 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19079 <span>{{1288323623006 | date:'medium'}}</span><br>
19080 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19081 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19082 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19083 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19084 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19085 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19087 <file name="protractor.js" type="protractor">
19088 it('should format date', function() {
19089 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19090 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19091 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19092 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19093 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19094 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19095 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19096 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19101 dateFilter.$inject = ['$locale'];
19102 function dateFilter($locale) {
19105 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19106 // 1 2 3 4 5 6 7 8 9 10 11
19107 function jsonStringToDate(string) {
19109 if (match = string.match(R_ISO8601_STR)) {
19110 var date = new Date(0),
19113 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19114 timeSetter = match[8] ? date.setUTCHours : date.setHours;
19117 tzHour = toInt(match[9] + match[10]);
19118 tzMin = toInt(match[9] + match[11]);
19120 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19121 var h = toInt(match[4] || 0) - tzHour;
19122 var m = toInt(match[5] || 0) - tzMin;
19123 var s = toInt(match[6] || 0);
19124 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19125 timeSetter.call(date, h, m, s, ms);
19132 return function(date, format, timezone) {
19137 format = format || 'mediumDate';
19138 format = $locale.DATETIME_FORMATS[format] || format;
19139 if (isString(date)) {
19140 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19143 if (isNumber(date)) {
19144 date = new Date(date);
19147 if (!isDate(date) || !isFinite(date.getTime())) {
19152 match = DATE_FORMATS_SPLIT.exec(format);
19154 parts = concat(parts, match, 1);
19155 format = parts.pop();
19157 parts.push(format);
19162 var dateTimezoneOffset = date.getTimezoneOffset();
19164 dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19165 date = convertTimezoneToLocal(date, timezone, true);
19167 forEach(parts, function(value) {
19168 fn = DATE_FORMATS[value];
19169 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19170 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19184 * Allows you to convert a JavaScript object into JSON string.
19186 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
19187 * the binding is automatically converted to JSON.
19189 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19190 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19191 * @returns {string} JSON string.
19196 <file name="index.html">
19197 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19198 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19200 <file name="protractor.js" type="protractor">
19201 it('should jsonify filtered objects', function() {
19202 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19203 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19209 function jsonFilter() {
19210 return function(object, spacing) {
19211 if (isUndefined(spacing)) {
19214 return toJson(object, spacing);
19224 * Converts string to lowercase.
19225 * @see angular.lowercase
19227 var lowercaseFilter = valueFn(lowercase);
19235 * Converts string to uppercase.
19236 * @see angular.uppercase
19238 var uppercaseFilter = valueFn(uppercase);
19246 * Creates a new array or string containing only a specified number of elements. The elements
19247 * are taken from either the beginning or the end of the source array, string or number, as specified by
19248 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19249 * converted to a string.
19251 * @param {Array|string|number} input Source array, string or number to be limited.
19252 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19253 * is positive, `limit` number of items from the beginning of the source array/string are copied.
19254 * If the number is negative, `limit` number of items from the end of the source array/string
19255 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19256 * the input will be returned unchanged.
19257 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19258 * indicates an offset from the end of `input`. Defaults to `0`.
19259 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19260 * had less than `limit` elements.
19263 <example module="limitToExample">
19264 <file name="index.html">
19266 angular.module('limitToExample', [])
19267 .controller('ExampleController', ['$scope', function($scope) {
19268 $scope.numbers = [1,2,3,4,5,6,7,8,9];
19269 $scope.letters = "abcdefghi";
19270 $scope.longNumber = 2345432342;
19271 $scope.numLimit = 3;
19272 $scope.letterLimit = 3;
19273 $scope.longNumberLimit = 3;
19276 <div ng-controller="ExampleController">
19278 Limit {{numbers}} to:
19279 <input type="number" step="1" ng-model="numLimit">
19281 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19283 Limit {{letters}} to:
19284 <input type="number" step="1" ng-model="letterLimit">
19286 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19288 Limit {{longNumber}} to:
19289 <input type="number" step="1" ng-model="longNumberLimit">
19291 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19294 <file name="protractor.js" type="protractor">
19295 var numLimitInput = element(by.model('numLimit'));
19296 var letterLimitInput = element(by.model('letterLimit'));
19297 var longNumberLimitInput = element(by.model('longNumberLimit'));
19298 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19299 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19300 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19302 it('should limit the number array to first three items', function() {
19303 expect(numLimitInput.getAttribute('value')).toBe('3');
19304 expect(letterLimitInput.getAttribute('value')).toBe('3');
19305 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19306 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19307 expect(limitedLetters.getText()).toEqual('Output letters: abc');
19308 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19311 // There is a bug in safari and protractor that doesn't like the minus key
19312 // it('should update the output when -3 is entered', function() {
19313 // numLimitInput.clear();
19314 // numLimitInput.sendKeys('-3');
19315 // letterLimitInput.clear();
19316 // letterLimitInput.sendKeys('-3');
19317 // longNumberLimitInput.clear();
19318 // longNumberLimitInput.sendKeys('-3');
19319 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19320 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19321 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19324 it('should not exceed the maximum size of input array', function() {
19325 numLimitInput.clear();
19326 numLimitInput.sendKeys('100');
19327 letterLimitInput.clear();
19328 letterLimitInput.sendKeys('100');
19329 longNumberLimitInput.clear();
19330 longNumberLimitInput.sendKeys('100');
19331 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19332 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19333 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19338 function limitToFilter() {
19339 return function(input, limit, begin) {
19340 if (Math.abs(Number(limit)) === Infinity) {
19341 limit = Number(limit);
19343 limit = toInt(limit);
19345 if (isNaN(limit)) return input;
19347 if (isNumber(input)) input = input.toString();
19348 if (!isArray(input) && !isString(input)) return input;
19350 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19351 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19354 return input.slice(begin, begin + limit);
19357 return input.slice(limit, input.length);
19359 return input.slice(Math.max(0, begin + limit), begin);
19371 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19372 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19373 * as expected, make sure they are actually being saved as numbers and not strings.
19375 * @param {Array} array The array to sort.
19376 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19377 * used by the comparator to determine the order of elements.
19381 * - `function`: Getter function. The result of this function will be sorted using the
19382 * `<`, `===`, `>` operator.
19383 * - `string`: An Angular expression. The result of this expression is used to compare elements
19384 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19385 * 3 first characters of a property called `name`). The result of a constant expression
19386 * is interpreted as a property name to be used in comparisons (for example `"special name"`
19387 * to sort object by the value of their `special name` property). An expression can be
19388 * optionally prefixed with `+` or `-` to control ascending or descending sort order
19389 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19390 * element itself is used to compare where sorting.
19391 * - `Array`: An array of function or string predicates. The first predicate in the array
19392 * is used for sorting, but when two items are equivalent, the next predicate is used.
19394 * If the predicate is missing or empty then it defaults to `'+'`.
19396 * @param {boolean=} reverse Reverse the order of the array.
19397 * @returns {Array} Sorted copy of the source array.
19401 * The example below demonstrates a simple ngRepeat, where the data is sorted
19402 * by age in descending order (predicate is set to `'-age'`).
19403 * `reverse` is not set, which means it defaults to `false`.
19404 <example module="orderByExample">
19405 <file name="index.html">
19407 angular.module('orderByExample', [])
19408 .controller('ExampleController', ['$scope', function($scope) {
19410 [{name:'John', phone:'555-1212', age:10},
19411 {name:'Mary', phone:'555-9876', age:19},
19412 {name:'Mike', phone:'555-4321', age:21},
19413 {name:'Adam', phone:'555-5678', age:35},
19414 {name:'Julie', phone:'555-8765', age:29}];
19417 <div ng-controller="ExampleController">
19418 <table class="friend">
19421 <th>Phone Number</th>
19424 <tr ng-repeat="friend in friends | orderBy:'-age'">
19425 <td>{{friend.name}}</td>
19426 <td>{{friend.phone}}</td>
19427 <td>{{friend.age}}</td>
19434 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19435 * as shown in the next example.
19437 <example module="orderByExample">
19438 <file name="index.html">
19440 angular.module('orderByExample', [])
19441 .controller('ExampleController', ['$scope', function($scope) {
19443 [{name:'John', phone:'555-1212', age:10},
19444 {name:'Mary', phone:'555-9876', age:19},
19445 {name:'Mike', phone:'555-4321', age:21},
19446 {name:'Adam', phone:'555-5678', age:35},
19447 {name:'Julie', phone:'555-8765', age:29}];
19448 $scope.predicate = 'age';
19449 $scope.reverse = true;
19450 $scope.order = function(predicate) {
19451 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19452 $scope.predicate = predicate;
19456 <style type="text/css">
19460 .sortorder.reverse:after {
19464 <div ng-controller="ExampleController">
19465 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19467 [ <a href="" ng-click="predicate=''">unsorted</a> ]
19468 <table class="friend">
19471 <a href="" ng-click="order('name')">Name</a>
19472 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19475 <a href="" ng-click="order('phone')">Phone Number</a>
19476 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19479 <a href="" ng-click="order('age')">Age</a>
19480 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19483 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19484 <td>{{friend.name}}</td>
19485 <td>{{friend.phone}}</td>
19486 <td>{{friend.age}}</td>
19493 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19494 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19495 * desired parameters.
19500 <example module="orderByExample">
19501 <file name="index.html">
19502 <div ng-controller="ExampleController">
19503 <table class="friend">
19505 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19506 (<a href="" ng-click="order('-name',false)">^</a>)</th>
19507 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19508 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19510 <tr ng-repeat="friend in friends">
19511 <td>{{friend.name}}</td>
19512 <td>{{friend.phone}}</td>
19513 <td>{{friend.age}}</td>
19519 <file name="script.js">
19520 angular.module('orderByExample', [])
19521 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19522 var orderBy = $filter('orderBy');
19524 { name: 'John', phone: '555-1212', age: 10 },
19525 { name: 'Mary', phone: '555-9876', age: 19 },
19526 { name: 'Mike', phone: '555-4321', age: 21 },
19527 { name: 'Adam', phone: '555-5678', age: 35 },
19528 { name: 'Julie', phone: '555-8765', age: 29 }
19530 $scope.order = function(predicate, reverse) {
19531 $scope.friends = orderBy($scope.friends, predicate, reverse);
19533 $scope.order('-age',false);
19538 orderByFilter.$inject = ['$parse'];
19539 function orderByFilter($parse) {
19540 return function(array, sortPredicate, reverseOrder) {
19542 if (!(isArrayLike(array))) return array;
19544 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19545 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19547 var predicates = processPredicates(sortPredicate, reverseOrder);
19548 // Add a predicate at the end that evaluates to the element index. This makes the
19549 // sort stable as it works as a tie-breaker when all the input predicates cannot
19550 // distinguish between two elements.
19551 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19553 // The next three lines are a version of a Swartzian Transform idiom from Perl
19554 // (sometimes called the Decorate-Sort-Undecorate idiom)
19555 // See https://en.wikipedia.org/wiki/Schwartzian_transform
19556 var compareValues = Array.prototype.map.call(array, getComparisonObject);
19557 compareValues.sort(doComparison);
19558 array = compareValues.map(function(item) { return item.value; });
19562 function getComparisonObject(value, index) {
19565 predicateValues: predicates.map(function(predicate) {
19566 return getPredicateValue(predicate.get(value), index);
19571 function doComparison(v1, v2) {
19573 for (var index=0, length = predicates.length; index < length; ++index) {
19574 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19581 function processPredicates(sortPredicate, reverseOrder) {
19582 reverseOrder = reverseOrder ? -1 : 1;
19583 return sortPredicate.map(function(predicate) {
19584 var descending = 1, get = identity;
19586 if (isFunction(predicate)) {
19588 } else if (isString(predicate)) {
19589 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19590 descending = predicate.charAt(0) == '-' ? -1 : 1;
19591 predicate = predicate.substring(1);
19593 if (predicate !== '') {
19594 get = $parse(predicate);
19595 if (get.constant) {
19597 get = function(value) { return value[key]; };
19601 return { get: get, descending: descending * reverseOrder };
19605 function isPrimitive(value) {
19606 switch (typeof value) {
19607 case 'number': /* falls through */
19608 case 'boolean': /* falls through */
19616 function objectValue(value, index) {
19617 // If `valueOf` is a valid function use that
19618 if (typeof value.valueOf === 'function') {
19619 value = value.valueOf();
19620 if (isPrimitive(value)) return value;
19622 // If `toString` is a valid function and not the one from `Object.prototype` use that
19623 if (hasCustomToString(value)) {
19624 value = value.toString();
19625 if (isPrimitive(value)) return value;
19627 // We have a basic object so we use the position of the object in the collection
19631 function getPredicateValue(value, index) {
19632 var type = typeof value;
19633 if (value === null) {
19636 } else if (type === 'string') {
19637 value = value.toLowerCase();
19638 } else if (type === 'object') {
19639 value = objectValue(value, index);
19641 return { value: value, type: type };
19644 function compare(v1, v2) {
19646 if (v1.type === v2.type) {
19647 if (v1.value !== v2.value) {
19648 result = v1.value < v2.value ? -1 : 1;
19651 result = v1.type < v2.type ? -1 : 1;
19657 function ngDirective(directive) {
19658 if (isFunction(directive)) {
19663 directive.restrict = directive.restrict || 'AC';
19664 return valueFn(directive);
19673 * Modifies the default behavior of the html A tag so that the default action is prevented when
19674 * the href attribute is empty.
19676 * This change permits the easy creation of action links with the `ngClick` directive
19677 * without changing the location or causing page reloads, e.g.:
19678 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19680 var htmlAnchorDirective = valueFn({
19682 compile: function(element, attr) {
19683 if (!attr.href && !attr.xlinkHref) {
19684 return function(scope, element) {
19685 // If the linked element is not an anchor tag anymore, do nothing
19686 if (element[0].nodeName.toLowerCase() !== 'a') return;
19688 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19689 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19690 'xlink:href' : 'href';
19691 element.on('click', function(event) {
19692 // if we have no href url, then don't navigate anywhere.
19693 if (!element.attr(href)) {
19694 event.preventDefault();
19709 * Using Angular markup like `{{hash}}` in an href attribute will
19710 * make the link go to the wrong URL if the user clicks it before
19711 * Angular has a chance to replace the `{{hash}}` markup with its
19712 * value. Until Angular replaces the markup the link will be broken
19713 * and will most likely return a 404 error. The `ngHref` directive
19714 * solves this problem.
19716 * The wrong way to write it:
19718 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19721 * The correct way to write it:
19723 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19727 * @param {template} ngHref any string which can contain `{{}}` markup.
19730 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19731 * in links and their different behaviors:
19733 <file name="index.html">
19734 <input ng-model="value" /><br />
19735 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19736 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19737 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19738 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19739 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19740 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19742 <file name="protractor.js" type="protractor">
19743 it('should execute ng-click but not reload when href without value', function() {
19744 element(by.id('link-1')).click();
19745 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19746 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19749 it('should execute ng-click but not reload when href empty string', function() {
19750 element(by.id('link-2')).click();
19751 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19752 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19755 it('should execute ng-click and change url when ng-href specified', function() {
19756 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19758 element(by.id('link-3')).click();
19760 // At this point, we navigate away from an Angular page, so we need
19761 // to use browser.driver to get the base webdriver.
19763 browser.wait(function() {
19764 return browser.driver.getCurrentUrl().then(function(url) {
19765 return url.match(/\/123$/);
19767 }, 5000, 'page should navigate to /123');
19770 it('should execute ng-click but not reload when href empty string and name specified', function() {
19771 element(by.id('link-4')).click();
19772 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19773 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19776 it('should execute ng-click but not reload when no href but name specified', function() {
19777 element(by.id('link-5')).click();
19778 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19779 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19782 it('should only change url when only ng-href', function() {
19783 element(by.model('value')).clear();
19784 element(by.model('value')).sendKeys('6');
19785 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19787 element(by.id('link-6')).click();
19789 // At this point, we navigate away from an Angular page, so we need
19790 // to use browser.driver to get the base webdriver.
19791 browser.wait(function() {
19792 return browser.driver.getCurrentUrl().then(function(url) {
19793 return url.match(/\/6$/);
19795 }, 5000, 'page should navigate to /6');
19808 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19809 * work right: The browser will fetch from the URL with the literal
19810 * text `{{hash}}` until Angular replaces the expression inside
19811 * `{{hash}}`. The `ngSrc` directive solves this problem.
19813 * The buggy way to write it:
19815 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19818 * The correct way to write it:
19820 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19824 * @param {template} ngSrc any string which can contain `{{}}` markup.
19834 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19835 * work right: The browser will fetch from the URL with the literal
19836 * text `{{hash}}` until Angular replaces the expression inside
19837 * `{{hash}}`. The `ngSrcset` directive solves this problem.
19839 * The buggy way to write it:
19841 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19844 * The correct way to write it:
19846 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19850 * @param {template} ngSrcset any string which can contain `{{}}` markup.
19861 * This directive sets the `disabled` attribute on the element if the
19862 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19864 * A special directive is necessary because we cannot use interpolation inside the `disabled`
19865 * attribute. The following example would make the button enabled on Chrome/Firefox
19866 * but not on older IEs:
19869 * <!-- See below for an example of ng-disabled being used correctly -->
19870 * <div ng-init="isDisabled = false">
19871 * <button disabled="{{isDisabled}}">Disabled</button>
19875 * This is because the HTML specification does not require browsers to preserve the values of
19876 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19877 * If we put an Angular interpolation expression into such an attribute then the
19878 * binding information would be lost when the browser removes the attribute.
19882 <file name="index.html">
19883 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19884 <button ng-model="button" ng-disabled="checked">Button</button>
19886 <file name="protractor.js" type="protractor">
19887 it('should toggle button', function() {
19888 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19889 element(by.model('checked')).click();
19890 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19896 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19897 * then the `disabled` attribute will be set on the element
19908 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19910 * Note that this directive should not be used together with {@link ngModel `ngModel`},
19911 * as this can lead to unexpected behavior.
19913 * ### Why do we need `ngChecked`?
19915 * The HTML specification does not require browsers to preserve the values of boolean attributes
19916 * such as checked. (Their presence means true and their absence means false.)
19917 * If we put an Angular interpolation expression into such an attribute then the
19918 * binding information would be lost when the browser removes the attribute.
19919 * The `ngChecked` directive solves this problem for the `checked` attribute.
19920 * This complementary directive is not removed by the browser and so provides
19921 * a permanent reliable place to store the binding information.
19924 <file name="index.html">
19925 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19926 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19928 <file name="protractor.js" type="protractor">
19929 it('should check both checkBoxes', function() {
19930 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19931 element(by.model('master')).click();
19932 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19938 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19939 * then the `checked` attribute will be set on the element
19950 * The HTML specification does not require browsers to preserve the values of boolean attributes
19951 * such as readonly. (Their presence means true and their absence means false.)
19952 * If we put an Angular interpolation expression into such an attribute then the
19953 * binding information would be lost when the browser removes the attribute.
19954 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19955 * This complementary directive is not removed by the browser and so provides
19956 * a permanent reliable place to store the binding information.
19959 <file name="index.html">
19960 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19961 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19963 <file name="protractor.js" type="protractor">
19964 it('should toggle readonly attr', function() {
19965 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19966 element(by.model('checked')).click();
19967 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19973 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19974 * then special attribute "readonly" will be set on the element
19985 * The HTML specification does not require browsers to preserve the values of boolean attributes
19986 * such as selected. (Their presence means true and their absence means false.)
19987 * If we put an Angular interpolation expression into such an attribute then the
19988 * binding information would be lost when the browser removes the attribute.
19989 * The `ngSelected` directive solves this problem for the `selected` attribute.
19990 * This complementary directive is not removed by the browser and so provides
19991 * a permanent reliable place to store the binding information.
19995 <file name="index.html">
19996 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19997 <select aria-label="ngSelected demo">
19998 <option>Hello!</option>
19999 <option id="greet" ng-selected="selected">Greetings!</option>
20002 <file name="protractor.js" type="protractor">
20003 it('should select Greetings!', function() {
20004 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20005 element(by.model('selected')).click();
20006 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20012 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20013 * then special attribute "selected" will be set on the element
20023 * The HTML specification does not require browsers to preserve the values of boolean attributes
20024 * such as open. (Their presence means true and their absence means false.)
20025 * If we put an Angular interpolation expression into such an attribute then the
20026 * binding information would be lost when the browser removes the attribute.
20027 * The `ngOpen` directive solves this problem for the `open` attribute.
20028 * This complementary directive is not removed by the browser and so provides
20029 * a permanent reliable place to store the binding information.
20032 <file name="index.html">
20033 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20034 <details id="details" ng-open="open">
20035 <summary>Show/Hide me</summary>
20038 <file name="protractor.js" type="protractor">
20039 it('should toggle open', function() {
20040 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20041 element(by.model('open')).click();
20042 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20048 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20049 * then special attribute "open" will be set on the element
20052 var ngAttributeAliasDirectives = {};
20054 // boolean attrs are evaluated
20055 forEach(BOOLEAN_ATTR, function(propName, attrName) {
20056 // binding to multiple is not supported
20057 if (propName == "multiple") return;
20059 function defaultLinkFn(scope, element, attr) {
20060 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20061 attr.$set(attrName, !!value);
20065 var normalized = directiveNormalize('ng-' + attrName);
20066 var linkFn = defaultLinkFn;
20068 if (propName === 'checked') {
20069 linkFn = function(scope, element, attr) {
20070 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20071 if (attr.ngModel !== attr[normalized]) {
20072 defaultLinkFn(scope, element, attr);
20077 ngAttributeAliasDirectives[normalized] = function() {
20086 // aliased input attrs are evaluated
20087 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20088 ngAttributeAliasDirectives[ngAttr] = function() {
20091 link: function(scope, element, attr) {
20092 //special case ngPattern when a literal regular expression value
20093 //is used as the expression (this way we don't have to watch anything).
20094 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20095 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20097 attr.$set("ngPattern", new RegExp(match[1], match[2]));
20102 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20103 attr.$set(ngAttr, value);
20110 // ng-src, ng-srcset, ng-href are interpolated
20111 forEach(['src', 'srcset', 'href'], function(attrName) {
20112 var normalized = directiveNormalize('ng-' + attrName);
20113 ngAttributeAliasDirectives[normalized] = function() {
20115 priority: 99, // it needs to run after the attributes are interpolated
20116 link: function(scope, element, attr) {
20117 var propName = attrName,
20120 if (attrName === 'href' &&
20121 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20122 name = 'xlinkHref';
20123 attr.$attr[name] = 'xlink:href';
20127 attr.$observe(normalized, function(value) {
20129 if (attrName === 'href') {
20130 attr.$set(name, null);
20135 attr.$set(name, value);
20137 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20138 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20139 // to set the property as well to achieve the desired effect.
20140 // we use attr[attrName] value since $set can sanitize the url.
20141 if (msie && propName) element.prop(propName, attr[name]);
20148 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20150 var nullFormCtrl = {
20152 $$renameControl: nullFormRenameControl,
20153 $removeControl: noop,
20154 $setValidity: noop,
20156 $setPristine: noop,
20157 $setSubmitted: noop
20159 SUBMITTED_CLASS = 'ng-submitted';
20161 function nullFormRenameControl(control, name) {
20162 control.$name = name;
20167 * @name form.FormController
20169 * @property {boolean} $pristine True if user has not interacted with the form yet.
20170 * @property {boolean} $dirty True if user has already interacted with the form.
20171 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20172 * @property {boolean} $invalid True if at least one containing control or form is invalid.
20173 * @property {boolean} $pending True if at least one containing control or form is pending.
20174 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20176 * @property {Object} $error Is an object hash, containing references to controls or
20177 * forms with failing validators, where:
20179 * - keys are validation tokens (error names),
20180 * - values are arrays of controls or forms that have a failing validator for given error name.
20182 * Built-in validation tokens:
20194 * - `datetimelocal`
20200 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20201 * such as being valid/invalid or dirty/pristine.
20203 * Each {@link ng.directive:form form} directive creates an instance
20204 * of `FormController`.
20207 //asks for $scope to fool the BC controller module
20208 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20209 function FormController(element, attrs, $scope, $animate, $interpolate) {
20215 form.$$success = {};
20216 form.$pending = undefined;
20217 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20218 form.$dirty = false;
20219 form.$pristine = true;
20220 form.$valid = true;
20221 form.$invalid = false;
20222 form.$submitted = false;
20223 form.$$parentForm = nullFormCtrl;
20227 * @name form.FormController#$rollbackViewValue
20230 * Rollback all form controls pending updates to the `$modelValue`.
20232 * Updates may be pending by a debounced event or because the input is waiting for a some future
20233 * event defined in `ng-model-options`. This method is typically needed by the reset button of
20234 * a form that uses `ng-model-options` to pend updates.
20236 form.$rollbackViewValue = function() {
20237 forEach(controls, function(control) {
20238 control.$rollbackViewValue();
20244 * @name form.FormController#$commitViewValue
20247 * Commit all form controls pending updates to the `$modelValue`.
20249 * Updates may be pending by a debounced event or because the input is waiting for a some future
20250 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20251 * usually handles calling this in response to input events.
20253 form.$commitViewValue = function() {
20254 forEach(controls, function(control) {
20255 control.$commitViewValue();
20261 * @name form.FormController#$addControl
20262 * @param {object} control control object, either a {@link form.FormController} or an
20263 * {@link ngModel.NgModelController}
20266 * Register a control with the form. Input elements using ngModelController do this automatically
20267 * when they are linked.
20269 * Note that the current state of the control will not be reflected on the new parent form. This
20270 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20273 * However, if the method is used programmatically, for example by adding dynamically created controls,
20274 * or controls that have been previously removed without destroying their corresponding DOM element,
20275 * it's the developers responsiblity to make sure the current state propagates to the parent form.
20277 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20278 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20280 form.$addControl = function(control) {
20281 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20282 // and not added to the scope. Now we throw an error.
20283 assertNotHasOwnProperty(control.$name, 'input');
20284 controls.push(control);
20286 if (control.$name) {
20287 form[control.$name] = control;
20290 control.$$parentForm = form;
20293 // Private API: rename a form control
20294 form.$$renameControl = function(control, newName) {
20295 var oldName = control.$name;
20297 if (form[oldName] === control) {
20298 delete form[oldName];
20300 form[newName] = control;
20301 control.$name = newName;
20306 * @name form.FormController#$removeControl
20307 * @param {object} control control object, either a {@link form.FormController} or an
20308 * {@link ngModel.NgModelController}
20311 * Deregister a control from the form.
20313 * Input elements using ngModelController do this automatically when they are destroyed.
20315 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20316 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20317 * different from case to case. For example, removing the only `$dirty` control from a form may or
20318 * may not mean that the form is still `$dirty`.
20320 form.$removeControl = function(control) {
20321 if (control.$name && form[control.$name] === control) {
20322 delete form[control.$name];
20324 forEach(form.$pending, function(value, name) {
20325 form.$setValidity(name, null, control);
20327 forEach(form.$error, function(value, name) {
20328 form.$setValidity(name, null, control);
20330 forEach(form.$$success, function(value, name) {
20331 form.$setValidity(name, null, control);
20334 arrayRemove(controls, control);
20335 control.$$parentForm = nullFormCtrl;
20341 * @name form.FormController#$setValidity
20344 * Sets the validity of a form control.
20346 * This method will also propagate to parent forms.
20348 addSetValidityMethod({
20351 set: function(object, property, controller) {
20352 var list = object[property];
20354 object[property] = [controller];
20356 var index = list.indexOf(controller);
20357 if (index === -1) {
20358 list.push(controller);
20362 unset: function(object, property, controller) {
20363 var list = object[property];
20367 arrayRemove(list, controller);
20368 if (list.length === 0) {
20369 delete object[property];
20377 * @name form.FormController#$setDirty
20380 * Sets the form to a dirty state.
20382 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20383 * state (ng-dirty class). This method will also propagate to parent forms.
20385 form.$setDirty = function() {
20386 $animate.removeClass(element, PRISTINE_CLASS);
20387 $animate.addClass(element, DIRTY_CLASS);
20388 form.$dirty = true;
20389 form.$pristine = false;
20390 form.$$parentForm.$setDirty();
20395 * @name form.FormController#$setPristine
20398 * Sets the form to its pristine state.
20400 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20401 * state (ng-pristine class). This method will also propagate to all the controls contained
20404 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20405 * saving or resetting it.
20407 form.$setPristine = function() {
20408 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20409 form.$dirty = false;
20410 form.$pristine = true;
20411 form.$submitted = false;
20412 forEach(controls, function(control) {
20413 control.$setPristine();
20419 * @name form.FormController#$setUntouched
20422 * Sets the form to its untouched state.
20424 * This method can be called to remove the 'ng-touched' class and set the form controls to their
20425 * untouched state (ng-untouched class).
20427 * Setting a form controls back to their untouched state is often useful when setting the form
20428 * back to its pristine state.
20430 form.$setUntouched = function() {
20431 forEach(controls, function(control) {
20432 control.$setUntouched();
20438 * @name form.FormController#$setSubmitted
20441 * Sets the form to its submitted state.
20443 form.$setSubmitted = function() {
20444 $animate.addClass(element, SUBMITTED_CLASS);
20445 form.$submitted = true;
20446 form.$$parentForm.$setSubmitted();
20456 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20457 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20458 * sub-group of controls needs to be determined.
20460 * Note: the purpose of `ngForm` is to group controls,
20461 * but not to be a replacement for the `<form>` tag with all of its capabilities
20462 * (e.g. posting to the server, ...).
20464 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20465 * related scope, under this name.
20475 * Directive that instantiates
20476 * {@link form.FormController FormController}.
20478 * If the `name` attribute is specified, the form controller is published onto the current scope under
20481 * # Alias: {@link ng.directive:ngForm `ngForm`}
20483 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20484 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20485 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20486 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
20487 * using Angular validation directives in forms that are dynamically generated using the
20488 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20489 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20490 * `ngForm` directive and nest these in an outer `form` element.
20494 * - `ng-valid` is set if the form is valid.
20495 * - `ng-invalid` is set if the form is invalid.
20496 * - `ng-pending` is set if the form is pending.
20497 * - `ng-pristine` is set if the form is pristine.
20498 * - `ng-dirty` is set if the form is dirty.
20499 * - `ng-submitted` is set if the form was submitted.
20501 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20504 * # Submitting a form and preventing the default action
20506 * Since the role of forms in client-side Angular applications is different than in classical
20507 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20508 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20509 * to handle the form submission in an application-specific way.
20511 * For this reason, Angular prevents the default action (form submission to the server) unless the
20512 * `<form>` element has an `action` attribute specified.
20514 * You can use one of the following two ways to specify what javascript method should be called when
20515 * a form is submitted:
20517 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20518 * - {@link ng.directive:ngClick ngClick} directive on the first
20519 * button or input field of type submit (input[type=submit])
20521 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20522 * or {@link ng.directive:ngClick ngClick} directives.
20523 * This is because of the following form submission rules in the HTML specification:
20525 * - If a form has only one input field then hitting enter in this field triggers form submit
20527 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20528 * doesn't trigger submit
20529 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20530 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20531 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20533 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20534 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20535 * to have access to the updated model.
20537 * ## Animation Hooks
20539 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20540 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20541 * other validations that are performed within the form. Animations in ngForm are similar to how
20542 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20543 * as JS animations.
20545 * The following example shows a simple way to utilize CSS transitions to style a form element
20546 * that has been rendered as invalid after it has been validated:
20549 * //be sure to include ngAnimate as a module to hook into more
20550 * //advanced animations
20552 * transition:0.5s linear all;
20553 * background: white;
20555 * .my-form.ng-invalid {
20562 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20563 <file name="index.html">
20565 angular.module('formExample', [])
20566 .controller('FormController', ['$scope', function($scope) {
20567 $scope.userType = 'guest';
20572 transition:all linear 0.5s;
20573 background: transparent;
20575 .my-form.ng-invalid {
20579 <form name="myForm" ng-controller="FormController" class="my-form">
20580 userType: <input name="input" ng-model="userType" required>
20581 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20582 <code>userType = {{userType}}</code><br>
20583 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20584 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20585 <code>myForm.$valid = {{myForm.$valid}}</code><br>
20586 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20589 <file name="protractor.js" type="protractor">
20590 it('should initialize to model', function() {
20591 var userType = element(by.binding('userType'));
20592 var valid = element(by.binding('myForm.input.$valid'));
20594 expect(userType.getText()).toContain('guest');
20595 expect(valid.getText()).toContain('true');
20598 it('should be invalid if empty', function() {
20599 var userType = element(by.binding('userType'));
20600 var valid = element(by.binding('myForm.input.$valid'));
20601 var userInput = element(by.model('userType'));
20604 userInput.sendKeys('');
20606 expect(userType.getText()).toEqual('userType =');
20607 expect(valid.getText()).toContain('false');
20612 * @param {string=} name Name of the form. If specified, the form controller will be published into
20613 * related scope, under this name.
20615 var formDirectiveFactory = function(isNgForm) {
20616 return ['$timeout', '$parse', function($timeout, $parse) {
20617 var formDirective = {
20619 restrict: isNgForm ? 'EAC' : 'E',
20620 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20621 controller: FormController,
20622 compile: function ngFormCompile(formElement, attr) {
20623 // Setup initial state of the control
20624 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20626 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20629 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20630 var controller = ctrls[0];
20632 // if `action` attr is not present on the form, prevent the default action (submission)
20633 if (!('action' in attr)) {
20634 // we can't use jq events because if a form is destroyed during submission the default
20635 // action is not prevented. see #1238
20637 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20638 // page reload if the form was destroyed by submission of the form via a click handler
20639 // on a button in the form. Looks like an IE9 specific bug.
20640 var handleFormSubmission = function(event) {
20641 scope.$apply(function() {
20642 controller.$commitViewValue();
20643 controller.$setSubmitted();
20646 event.preventDefault();
20649 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20651 // unregister the preventDefault listener so that we don't not leak memory but in a
20652 // way that will achieve the prevention of the default action.
20653 formElement.on('$destroy', function() {
20654 $timeout(function() {
20655 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20660 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20661 parentFormCtrl.$addControl(controller);
20663 var setter = nameAttr ? getSetter(controller.$name) : noop;
20666 setter(scope, controller);
20667 attr.$observe(nameAttr, function(newValue) {
20668 if (controller.$name === newValue) return;
20669 setter(scope, undefined);
20670 controller.$$parentForm.$$renameControl(controller, newValue);
20671 setter = getSetter(controller.$name);
20672 setter(scope, controller);
20675 formElement.on('$destroy', function() {
20676 controller.$$parentForm.$removeControl(controller);
20677 setter(scope, undefined);
20678 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20685 return formDirective;
20687 function getSetter(expression) {
20688 if (expression === '') {
20689 //create an assignable expression, so forms with an empty name can be renamed later
20690 return $parse('this[""]').assign;
20692 return $parse(expression).assign || noop;
20697 var formDirective = formDirectiveFactory();
20698 var ngFormDirective = formDirectiveFactory(true);
20700 /* global VALID_CLASS: false,
20701 INVALID_CLASS: false,
20702 PRISTINE_CLASS: false,
20703 DIRTY_CLASS: false,
20704 UNTOUCHED_CLASS: false,
20705 TOUCHED_CLASS: false,
20706 ngModelMinErr: false,
20709 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20710 var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
20711 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20712 var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20713 var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20714 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20715 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20716 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20717 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20718 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20719 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20725 * @name input[text]
20728 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20731 * @param {string} ngModel Assignable angular expression to data-bind to.
20732 * @param {string=} name Property name of the form under which the control is published.
20733 * @param {string=} required Adds `required` validation error key if the value is not entered.
20734 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20735 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20736 * `required` when you want to data-bind to the `required` attribute.
20737 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20739 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20740 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20742 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20743 * that contains the regular expression body that will be converted to a regular expression
20744 * as in the ngPattern directive.
20745 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20746 * a RegExp found by evaluating the Angular expression given in the attribute value.
20747 * If the expression evaluates to a RegExp object, then this is used directly.
20748 * If the expression evaluates to a string, then it will be converted to a RegExp
20749 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20750 * `new RegExp('^abc$')`.<br />
20751 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20752 * start at the index of the last search's match, thus not taking the whole input value into
20754 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20755 * interaction with the input element.
20756 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20757 * This parameter is ignored for input[type=password] controls, which will never trim the
20761 <example name="text-input-directive" module="textInputExample">
20762 <file name="index.html">
20764 angular.module('textInputExample', [])
20765 .controller('ExampleController', ['$scope', function($scope) {
20768 word: /^\s*\w*\s*$/
20772 <form name="myForm" ng-controller="ExampleController">
20773 <label>Single word:
20774 <input type="text" name="input" ng-model="example.text"
20775 ng-pattern="example.word" required ng-trim="false">
20778 <span class="error" ng-show="myForm.input.$error.required">
20780 <span class="error" ng-show="myForm.input.$error.pattern">
20781 Single word only!</span>
20783 <tt>text = {{example.text}}</tt><br/>
20784 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20785 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20786 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20787 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20790 <file name="protractor.js" type="protractor">
20791 var text = element(by.binding('example.text'));
20792 var valid = element(by.binding('myForm.input.$valid'));
20793 var input = element(by.model('example.text'));
20795 it('should initialize to model', function() {
20796 expect(text.getText()).toContain('guest');
20797 expect(valid.getText()).toContain('true');
20800 it('should be invalid if empty', function() {
20802 input.sendKeys('');
20804 expect(text.getText()).toEqual('text =');
20805 expect(valid.getText()).toContain('false');
20808 it('should be invalid if multi word', function() {
20810 input.sendKeys('hello world');
20812 expect(valid.getText()).toContain('false');
20817 'text': textInputType,
20821 * @name input[date]
20824 * Input with date validation and transformation. In browsers that do not yet support
20825 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20826 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20827 * modern browsers do not yet support this input type, it is important to provide cues to users on the
20828 * expected input format via a placeholder or label.
20830 * The model must always be a Date object, otherwise Angular will throw an error.
20831 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20833 * The timezone to be used to read/write the `Date` instance in the model can be defined using
20834 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20836 * @param {string} ngModel Assignable angular expression to data-bind to.
20837 * @param {string=} name Property name of the form under which the control is published.
20838 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20839 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20840 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20841 * constraint validation.
20842 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20843 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20844 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20845 * constraint validation.
20846 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20847 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20848 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20849 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20850 * @param {string=} required Sets `required` validation error key if the value is not entered.
20851 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20852 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20853 * `required` when you want to data-bind to the `required` attribute.
20854 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20855 * interaction with the input element.
20858 <example name="date-input-directive" module="dateInputExample">
20859 <file name="index.html">
20861 angular.module('dateInputExample', [])
20862 .controller('DateController', ['$scope', function($scope) {
20864 value: new Date(2013, 9, 22)
20868 <form name="myForm" ng-controller="DateController as dateCtrl">
20869 <label for="exampleInput">Pick a date in 2013:</label>
20870 <input type="date" id="exampleInput" name="input" ng-model="example.value"
20871 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20873 <span class="error" ng-show="myForm.input.$error.required">
20875 <span class="error" ng-show="myForm.input.$error.date">
20876 Not a valid date!</span>
20878 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20879 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20880 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20881 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20882 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20885 <file name="protractor.js" type="protractor">
20886 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20887 var valid = element(by.binding('myForm.input.$valid'));
20888 var input = element(by.model('example.value'));
20890 // currently protractor/webdriver does not support
20891 // sending keys to all known HTML5 input controls
20892 // for various browsers (see https://github.com/angular/protractor/issues/562).
20893 function setInput(val) {
20894 // set the value of the element and force validation.
20895 var scr = "var ipt = document.getElementById('exampleInput'); " +
20896 "ipt.value = '" + val + "';" +
20897 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20898 browser.executeScript(scr);
20901 it('should initialize to model', function() {
20902 expect(value.getText()).toContain('2013-10-22');
20903 expect(valid.getText()).toContain('myForm.input.$valid = true');
20906 it('should be invalid if empty', function() {
20908 expect(value.getText()).toEqual('value =');
20909 expect(valid.getText()).toContain('myForm.input.$valid = false');
20912 it('should be invalid if over max', function() {
20913 setInput('2015-01-01');
20914 expect(value.getText()).toContain('');
20915 expect(valid.getText()).toContain('myForm.input.$valid = false');
20920 'date': createDateInputType('date', DATE_REGEXP,
20921 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20926 * @name input[datetime-local]
20929 * Input with datetime validation and transformation. In browsers that do not yet support
20930 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20931 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20933 * The model must always be a Date object, otherwise Angular will throw an error.
20934 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20936 * The timezone to be used to read/write the `Date` instance in the model can be defined using
20937 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20939 * @param {string} ngModel Assignable angular expression to data-bind to.
20940 * @param {string=} name Property name of the form under which the control is published.
20941 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20942 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20943 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20944 * Note that `min` will also add native HTML5 constraint validation.
20945 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20946 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20947 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20948 * Note that `max` will also add native HTML5 constraint validation.
20949 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20950 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20951 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20952 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20953 * @param {string=} required Sets `required` validation error key if the value is not entered.
20954 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20955 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20956 * `required` when you want to data-bind to the `required` attribute.
20957 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20958 * interaction with the input element.
20961 <example name="datetimelocal-input-directive" module="dateExample">
20962 <file name="index.html">
20964 angular.module('dateExample', [])
20965 .controller('DateController', ['$scope', function($scope) {
20967 value: new Date(2010, 11, 28, 14, 57)
20971 <form name="myForm" ng-controller="DateController as dateCtrl">
20972 <label for="exampleInput">Pick a date between in 2013:</label>
20973 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20974 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20976 <span class="error" ng-show="myForm.input.$error.required">
20978 <span class="error" ng-show="myForm.input.$error.datetimelocal">
20979 Not a valid date!</span>
20981 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20982 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20983 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20984 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20985 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20988 <file name="protractor.js" type="protractor">
20989 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20990 var valid = element(by.binding('myForm.input.$valid'));
20991 var input = element(by.model('example.value'));
20993 // currently protractor/webdriver does not support
20994 // sending keys to all known HTML5 input controls
20995 // for various browsers (https://github.com/angular/protractor/issues/562).
20996 function setInput(val) {
20997 // set the value of the element and force validation.
20998 var scr = "var ipt = document.getElementById('exampleInput'); " +
20999 "ipt.value = '" + val + "';" +
21000 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21001 browser.executeScript(scr);
21004 it('should initialize to model', function() {
21005 expect(value.getText()).toContain('2010-12-28T14:57:00');
21006 expect(valid.getText()).toContain('myForm.input.$valid = true');
21009 it('should be invalid if empty', function() {
21011 expect(value.getText()).toEqual('value =');
21012 expect(valid.getText()).toContain('myForm.input.$valid = false');
21015 it('should be invalid if over max', function() {
21016 setInput('2015-01-01T23:59:00');
21017 expect(value.getText()).toContain('');
21018 expect(valid.getText()).toContain('myForm.input.$valid = false');
21023 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21024 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21025 'yyyy-MM-ddTHH:mm:ss.sss'),
21029 * @name input[time]
21032 * Input with time validation and transformation. In browsers that do not yet support
21033 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21034 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21035 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21037 * The model must always be a Date object, otherwise Angular will throw an error.
21038 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21040 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21041 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21043 * @param {string} ngModel Assignable angular expression to data-bind to.
21044 * @param {string=} name Property name of the form under which the control is published.
21045 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21046 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21047 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21048 * native HTML5 constraint validation.
21049 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21050 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21051 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21052 * native HTML5 constraint validation.
21053 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21054 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21055 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21056 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21057 * @param {string=} required Sets `required` validation error key if the value is not entered.
21058 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21059 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21060 * `required` when you want to data-bind to the `required` attribute.
21061 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21062 * interaction with the input element.
21065 <example name="time-input-directive" module="timeExample">
21066 <file name="index.html">
21068 angular.module('timeExample', [])
21069 .controller('DateController', ['$scope', function($scope) {
21071 value: new Date(1970, 0, 1, 14, 57, 0)
21075 <form name="myForm" ng-controller="DateController as dateCtrl">
21076 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21077 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21078 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21080 <span class="error" ng-show="myForm.input.$error.required">
21082 <span class="error" ng-show="myForm.input.$error.time">
21083 Not a valid date!</span>
21085 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21086 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21087 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21088 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21089 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21092 <file name="protractor.js" type="protractor">
21093 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21094 var valid = element(by.binding('myForm.input.$valid'));
21095 var input = element(by.model('example.value'));
21097 // currently protractor/webdriver does not support
21098 // sending keys to all known HTML5 input controls
21099 // for various browsers (https://github.com/angular/protractor/issues/562).
21100 function setInput(val) {
21101 // set the value of the element and force validation.
21102 var scr = "var ipt = document.getElementById('exampleInput'); " +
21103 "ipt.value = '" + val + "';" +
21104 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21105 browser.executeScript(scr);
21108 it('should initialize to model', function() {
21109 expect(value.getText()).toContain('14:57:00');
21110 expect(valid.getText()).toContain('myForm.input.$valid = true');
21113 it('should be invalid if empty', function() {
21115 expect(value.getText()).toEqual('value =');
21116 expect(valid.getText()).toContain('myForm.input.$valid = false');
21119 it('should be invalid if over max', function() {
21120 setInput('23:59:00');
21121 expect(value.getText()).toContain('');
21122 expect(valid.getText()).toContain('myForm.input.$valid = false');
21127 'time': createDateInputType('time', TIME_REGEXP,
21128 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21133 * @name input[week]
21136 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21137 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21138 * week format (yyyy-W##), for example: `2013-W02`.
21140 * The model must always be a Date object, otherwise Angular will throw an error.
21141 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21143 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21144 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21146 * @param {string} ngModel Assignable angular expression to data-bind to.
21147 * @param {string=} name Property name of the form under which the control is published.
21148 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21149 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21150 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21151 * native HTML5 constraint validation.
21152 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21153 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21154 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21155 * native HTML5 constraint validation.
21156 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21157 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21158 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21159 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21160 * @param {string=} required Sets `required` validation error key if the value is not entered.
21161 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21162 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21163 * `required` when you want to data-bind to the `required` attribute.
21164 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21165 * interaction with the input element.
21168 <example name="week-input-directive" module="weekExample">
21169 <file name="index.html">
21171 angular.module('weekExample', [])
21172 .controller('DateController', ['$scope', function($scope) {
21174 value: new Date(2013, 0, 3)
21178 <form name="myForm" ng-controller="DateController as dateCtrl">
21179 <label>Pick a date between in 2013:
21180 <input id="exampleInput" type="week" name="input" ng-model="example.value"
21181 placeholder="YYYY-W##" min="2012-W32"
21182 max="2013-W52" required />
21185 <span class="error" ng-show="myForm.input.$error.required">
21187 <span class="error" ng-show="myForm.input.$error.week">
21188 Not a valid date!</span>
21190 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21191 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21192 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21193 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21194 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21197 <file name="protractor.js" type="protractor">
21198 var value = element(by.binding('example.value | date: "yyyy-Www"'));
21199 var valid = element(by.binding('myForm.input.$valid'));
21200 var input = element(by.model('example.value'));
21202 // currently protractor/webdriver does not support
21203 // sending keys to all known HTML5 input controls
21204 // for various browsers (https://github.com/angular/protractor/issues/562).
21205 function setInput(val) {
21206 // set the value of the element and force validation.
21207 var scr = "var ipt = document.getElementById('exampleInput'); " +
21208 "ipt.value = '" + val + "';" +
21209 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21210 browser.executeScript(scr);
21213 it('should initialize to model', function() {
21214 expect(value.getText()).toContain('2013-W01');
21215 expect(valid.getText()).toContain('myForm.input.$valid = true');
21218 it('should be invalid if empty', function() {
21220 expect(value.getText()).toEqual('value =');
21221 expect(valid.getText()).toContain('myForm.input.$valid = false');
21224 it('should be invalid if over max', function() {
21225 setInput('2015-W01');
21226 expect(value.getText()).toContain('');
21227 expect(valid.getText()).toContain('myForm.input.$valid = false');
21232 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21236 * @name input[month]
21239 * Input with month validation and transformation. In browsers that do not yet support
21240 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21241 * month format (yyyy-MM), for example: `2009-01`.
21243 * The model must always be a Date object, otherwise Angular will throw an error.
21244 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21245 * If the model is not set to the first of the month, the next view to model update will set it
21246 * to the first of the month.
21248 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21249 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21251 * @param {string} ngModel Assignable angular expression to data-bind to.
21252 * @param {string=} name Property name of the form under which the control is published.
21253 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21254 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21255 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21256 * native HTML5 constraint validation.
21257 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21258 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21259 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21260 * native HTML5 constraint validation.
21261 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21262 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21263 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21264 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21266 * @param {string=} required Sets `required` validation error key if the value is not entered.
21267 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21268 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21269 * `required` when you want to data-bind to the `required` attribute.
21270 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21271 * interaction with the input element.
21274 <example name="month-input-directive" module="monthExample">
21275 <file name="index.html">
21277 angular.module('monthExample', [])
21278 .controller('DateController', ['$scope', function($scope) {
21280 value: new Date(2013, 9, 1)
21284 <form name="myForm" ng-controller="DateController as dateCtrl">
21285 <label for="exampleInput">Pick a month in 2013:</label>
21286 <input id="exampleInput" type="month" name="input" ng-model="example.value"
21287 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21289 <span class="error" ng-show="myForm.input.$error.required">
21291 <span class="error" ng-show="myForm.input.$error.month">
21292 Not a valid month!</span>
21294 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21295 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21296 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21297 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21298 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21301 <file name="protractor.js" type="protractor">
21302 var value = element(by.binding('example.value | date: "yyyy-MM"'));
21303 var valid = element(by.binding('myForm.input.$valid'));
21304 var input = element(by.model('example.value'));
21306 // currently protractor/webdriver does not support
21307 // sending keys to all known HTML5 input controls
21308 // for various browsers (https://github.com/angular/protractor/issues/562).
21309 function setInput(val) {
21310 // set the value of the element and force validation.
21311 var scr = "var ipt = document.getElementById('exampleInput'); " +
21312 "ipt.value = '" + val + "';" +
21313 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21314 browser.executeScript(scr);
21317 it('should initialize to model', function() {
21318 expect(value.getText()).toContain('2013-10');
21319 expect(valid.getText()).toContain('myForm.input.$valid = true');
21322 it('should be invalid if empty', function() {
21324 expect(value.getText()).toEqual('value =');
21325 expect(valid.getText()).toContain('myForm.input.$valid = false');
21328 it('should be invalid if over max', function() {
21329 setInput('2015-01');
21330 expect(value.getText()).toContain('');
21331 expect(valid.getText()).toContain('myForm.input.$valid = false');
21336 'month': createDateInputType('month', MONTH_REGEXP,
21337 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21342 * @name input[number]
21345 * Text input with number validation and transformation. Sets the `number` validation
21346 * error if not a valid number.
21348 * <div class="alert alert-warning">
21349 * The model must always be of type `number` otherwise Angular will throw an error.
21350 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21351 * error docs for more information and an example of how to convert your model if necessary.
21354 * ## Issues with HTML5 constraint validation
21356 * In browsers that follow the
21357 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21358 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21359 * If a non-number is entered in the input, the browser will report the value as an empty string,
21360 * which means the view / model values in `ngModel` and subsequently the scope value
21361 * will also be an empty string.
21364 * @param {string} ngModel Assignable angular expression to data-bind to.
21365 * @param {string=} name Property name of the form under which the control is published.
21366 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21367 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21368 * @param {string=} required Sets `required` validation error key if the value is not entered.
21369 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21370 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21371 * `required` when you want to data-bind to the `required` attribute.
21372 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21374 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21375 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21377 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21378 * that contains the regular expression body that will be converted to a regular expression
21379 * as in the ngPattern directive.
21380 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21381 * a RegExp found by evaluating the Angular expression given in the attribute value.
21382 * If the expression evaluates to a RegExp object, then this is used directly.
21383 * If the expression evaluates to a string, then it will be converted to a RegExp
21384 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21385 * `new RegExp('^abc$')`.<br />
21386 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21387 * start at the index of the last search's match, thus not taking the whole input value into
21389 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21390 * interaction with the input element.
21393 <example name="number-input-directive" module="numberExample">
21394 <file name="index.html">
21396 angular.module('numberExample', [])
21397 .controller('ExampleController', ['$scope', function($scope) {
21403 <form name="myForm" ng-controller="ExampleController">
21405 <input type="number" name="input" ng-model="example.value"
21406 min="0" max="99" required>
21409 <span class="error" ng-show="myForm.input.$error.required">
21411 <span class="error" ng-show="myForm.input.$error.number">
21412 Not valid number!</span>
21414 <tt>value = {{example.value}}</tt><br/>
21415 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21416 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21417 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21418 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21421 <file name="protractor.js" type="protractor">
21422 var value = element(by.binding('example.value'));
21423 var valid = element(by.binding('myForm.input.$valid'));
21424 var input = element(by.model('example.value'));
21426 it('should initialize to model', function() {
21427 expect(value.getText()).toContain('12');
21428 expect(valid.getText()).toContain('true');
21431 it('should be invalid if empty', function() {
21433 input.sendKeys('');
21434 expect(value.getText()).toEqual('value =');
21435 expect(valid.getText()).toContain('false');
21438 it('should be invalid if over max', function() {
21440 input.sendKeys('123');
21441 expect(value.getText()).toEqual('value =');
21442 expect(valid.getText()).toContain('false');
21447 'number': numberInputType,
21455 * Text input with URL validation. Sets the `url` validation error key if the content is not a
21458 * <div class="alert alert-warning">
21459 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21460 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21461 * the built-in validators (see the {@link guide/forms Forms guide})
21464 * @param {string} ngModel Assignable angular expression to data-bind to.
21465 * @param {string=} name Property name of the form under which the control is published.
21466 * @param {string=} required Sets `required` validation error key if the value is not entered.
21467 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21468 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21469 * `required` when you want to data-bind to the `required` attribute.
21470 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21472 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21473 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21475 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21476 * that contains the regular expression body that will be converted to a regular expression
21477 * as in the ngPattern directive.
21478 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21479 * a RegExp found by evaluating the Angular expression given in the attribute value.
21480 * If the expression evaluates to a RegExp object, then this is used directly.
21481 * If the expression evaluates to a string, then it will be converted to a RegExp
21482 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21483 * `new RegExp('^abc$')`.<br />
21484 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21485 * start at the index of the last search's match, thus not taking the whole input value into
21487 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21488 * interaction with the input element.
21491 <example name="url-input-directive" module="urlExample">
21492 <file name="index.html">
21494 angular.module('urlExample', [])
21495 .controller('ExampleController', ['$scope', function($scope) {
21497 text: 'http://google.com'
21501 <form name="myForm" ng-controller="ExampleController">
21503 <input type="url" name="input" ng-model="url.text" required>
21506 <span class="error" ng-show="myForm.input.$error.required">
21508 <span class="error" ng-show="myForm.input.$error.url">
21509 Not valid url!</span>
21511 <tt>text = {{url.text}}</tt><br/>
21512 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21513 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21514 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21515 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21516 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21519 <file name="protractor.js" type="protractor">
21520 var text = element(by.binding('url.text'));
21521 var valid = element(by.binding('myForm.input.$valid'));
21522 var input = element(by.model('url.text'));
21524 it('should initialize to model', function() {
21525 expect(text.getText()).toContain('http://google.com');
21526 expect(valid.getText()).toContain('true');
21529 it('should be invalid if empty', function() {
21531 input.sendKeys('');
21533 expect(text.getText()).toEqual('text =');
21534 expect(valid.getText()).toContain('false');
21537 it('should be invalid if not url', function() {
21539 input.sendKeys('box');
21541 expect(valid.getText()).toContain('false');
21546 'url': urlInputType,
21551 * @name input[email]
21554 * Text input with email validation. Sets the `email` validation error key if not a valid email
21557 * <div class="alert alert-warning">
21558 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21559 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21560 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21563 * @param {string} ngModel Assignable angular expression to data-bind to.
21564 * @param {string=} name Property name of the form under which the control is published.
21565 * @param {string=} required Sets `required` validation error key if the value is not entered.
21566 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21567 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21568 * `required` when you want to data-bind to the `required` attribute.
21569 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21571 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21572 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21574 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21575 * that contains the regular expression body that will be converted to a regular expression
21576 * as in the ngPattern directive.
21577 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21578 * a RegExp found by evaluating the Angular expression given in the attribute value.
21579 * If the expression evaluates to a RegExp object, then this is used directly.
21580 * If the expression evaluates to a string, then it will be converted to a RegExp
21581 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21582 * `new RegExp('^abc$')`.<br />
21583 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21584 * start at the index of the last search's match, thus not taking the whole input value into
21586 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21587 * interaction with the input element.
21590 <example name="email-input-directive" module="emailExample">
21591 <file name="index.html">
21593 angular.module('emailExample', [])
21594 .controller('ExampleController', ['$scope', function($scope) {
21596 text: 'me@example.com'
21600 <form name="myForm" ng-controller="ExampleController">
21602 <input type="email" name="input" ng-model="email.text" required>
21605 <span class="error" ng-show="myForm.input.$error.required">
21607 <span class="error" ng-show="myForm.input.$error.email">
21608 Not valid email!</span>
21610 <tt>text = {{email.text}}</tt><br/>
21611 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21612 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21613 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21614 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21615 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21618 <file name="protractor.js" type="protractor">
21619 var text = element(by.binding('email.text'));
21620 var valid = element(by.binding('myForm.input.$valid'));
21621 var input = element(by.model('email.text'));
21623 it('should initialize to model', function() {
21624 expect(text.getText()).toContain('me@example.com');
21625 expect(valid.getText()).toContain('true');
21628 it('should be invalid if empty', function() {
21630 input.sendKeys('');
21631 expect(text.getText()).toEqual('text =');
21632 expect(valid.getText()).toContain('false');
21635 it('should be invalid if not email', function() {
21637 input.sendKeys('xxx');
21639 expect(valid.getText()).toContain('false');
21644 'email': emailInputType,
21649 * @name input[radio]
21652 * HTML radio button.
21654 * @param {string} ngModel Assignable angular expression to data-bind to.
21655 * @param {string} value The value to which the `ngModel` expression should be set when selected.
21656 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21657 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21658 * @param {string=} name Property name of the form under which the control is published.
21659 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21660 * interaction with the input element.
21661 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21662 * is selected. Should be used instead of the `value` attribute if you need
21663 * a non-string `ngModel` (`boolean`, `array`, ...).
21666 <example name="radio-input-directive" module="radioExample">
21667 <file name="index.html">
21669 angular.module('radioExample', [])
21670 .controller('ExampleController', ['$scope', function($scope) {
21674 $scope.specialValue = {
21680 <form name="myForm" ng-controller="ExampleController">
21682 <input type="radio" ng-model="color.name" value="red">
21686 <input type="radio" ng-model="color.name" ng-value="specialValue">
21690 <input type="radio" ng-model="color.name" value="blue">
21693 <tt>color = {{color.name | json}}</tt><br/>
21695 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21697 <file name="protractor.js" type="protractor">
21698 it('should change state', function() {
21699 var color = element(by.binding('color.name'));
21701 expect(color.getText()).toContain('blue');
21703 element.all(by.model('color.name')).get(0).click();
21705 expect(color.getText()).toContain('red');
21710 'radio': radioInputType,
21715 * @name input[checkbox]
21720 * @param {string} ngModel Assignable angular expression to data-bind to.
21721 * @param {string=} name Property name of the form under which the control is published.
21722 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21723 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21724 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21725 * interaction with the input element.
21728 <example name="checkbox-input-directive" module="checkboxExample">
21729 <file name="index.html">
21731 angular.module('checkboxExample', [])
21732 .controller('ExampleController', ['$scope', function($scope) {
21733 $scope.checkboxModel = {
21739 <form name="myForm" ng-controller="ExampleController">
21741 <input type="checkbox" ng-model="checkboxModel.value1">
21744 <input type="checkbox" ng-model="checkboxModel.value2"
21745 ng-true-value="'YES'" ng-false-value="'NO'">
21747 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21748 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21751 <file name="protractor.js" type="protractor">
21752 it('should change state', function() {
21753 var value1 = element(by.binding('checkboxModel.value1'));
21754 var value2 = element(by.binding('checkboxModel.value2'));
21756 expect(value1.getText()).toContain('true');
21757 expect(value2.getText()).toContain('YES');
21759 element(by.model('checkboxModel.value1')).click();
21760 element(by.model('checkboxModel.value2')).click();
21762 expect(value1.getText()).toContain('false');
21763 expect(value2.getText()).toContain('NO');
21768 'checkbox': checkboxInputType,
21777 function stringBasedInputType(ctrl) {
21778 ctrl.$formatters.push(function(value) {
21779 return ctrl.$isEmpty(value) ? value : value.toString();
21783 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21784 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21785 stringBasedInputType(ctrl);
21788 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21789 var type = lowercase(element[0].type);
21791 // In composition mode, users are still inputing intermediate text buffer,
21792 // hold the listener until composition is done.
21793 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21794 if (!$sniffer.android) {
21795 var composing = false;
21797 element.on('compositionstart', function(data) {
21801 element.on('compositionend', function() {
21807 var listener = function(ev) {
21809 $browser.defer.cancel(timeout);
21812 if (composing) return;
21813 var value = element.val(),
21814 event = ev && ev.type;
21816 // By default we will trim the value
21817 // If the attribute ng-trim exists we will avoid trimming
21818 // If input type is 'password', the value is never trimmed
21819 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21820 value = trim(value);
21823 // If a control is suffering from bad input (due to native validators), browsers discard its
21824 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21825 // control's value is the same empty value twice in a row.
21826 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21827 ctrl.$setViewValue(value, event);
21831 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21832 // input event on backspace, delete or cut
21833 if ($sniffer.hasEvent('input')) {
21834 element.on('input', listener);
21838 var deferListener = function(ev, input, origValue) {
21840 timeout = $browser.defer(function() {
21842 if (!input || input.value !== origValue) {
21849 element.on('keydown', function(event) {
21850 var key = event.keyCode;
21853 // command modifiers arrows
21854 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21856 deferListener(event, this, this.value);
21859 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21860 if ($sniffer.hasEvent('paste')) {
21861 element.on('paste cut', deferListener);
21865 // if user paste into input using mouse on older browser
21866 // or form autocomplete on newer browser, we need "change" event to catch it
21867 element.on('change', listener);
21869 ctrl.$render = function() {
21870 // Workaround for Firefox validation #12102.
21871 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21872 if (element.val() !== value) {
21873 element.val(value);
21878 function weekParser(isoWeek, existingDate) {
21879 if (isDate(isoWeek)) {
21883 if (isString(isoWeek)) {
21884 WEEK_REGEXP.lastIndex = 0;
21885 var parts = WEEK_REGEXP.exec(isoWeek);
21887 var year = +parts[1],
21893 firstThurs = getFirstThursdayOfYear(year),
21894 addDays = (week - 1) * 7;
21896 if (existingDate) {
21897 hours = existingDate.getHours();
21898 minutes = existingDate.getMinutes();
21899 seconds = existingDate.getSeconds();
21900 milliseconds = existingDate.getMilliseconds();
21903 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21910 function createDateParser(regexp, mapping) {
21911 return function(iso, date) {
21918 if (isString(iso)) {
21919 // When a date is JSON'ified to wraps itself inside of an extra
21920 // set of double quotes. This makes the date parsing code unable
21921 // to match the date string and parse it as a date.
21922 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21923 iso = iso.substring(1, iso.length - 1);
21925 if (ISO_DATE_REGEXP.test(iso)) {
21926 return new Date(iso);
21928 regexp.lastIndex = 0;
21929 parts = regexp.exec(iso);
21935 yyyy: date.getFullYear(),
21936 MM: date.getMonth() + 1,
21937 dd: date.getDate(),
21938 HH: date.getHours(),
21939 mm: date.getMinutes(),
21940 ss: date.getSeconds(),
21941 sss: date.getMilliseconds() / 1000
21944 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21947 forEach(parts, function(part, index) {
21948 if (index < mapping.length) {
21949 map[mapping[index]] = +part;
21952 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21960 function createDateInputType(type, regexp, parseDate, format) {
21961 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21962 badInputChecker(scope, element, attr, ctrl);
21963 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21964 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21967 ctrl.$$parserName = type;
21968 ctrl.$parsers.push(function(value) {
21969 if (ctrl.$isEmpty(value)) return null;
21970 if (regexp.test(value)) {
21971 // Note: We cannot read ctrl.$modelValue, as there might be a different
21972 // parser/formatter in the processing chain so that the model
21973 // contains some different data format!
21974 var parsedDate = parseDate(value, previousDate);
21976 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21983 ctrl.$formatters.push(function(value) {
21984 if (value && !isDate(value)) {
21985 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21987 if (isValidDate(value)) {
21988 previousDate = value;
21989 if (previousDate && timezone) {
21990 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21992 return $filter('date')(value, format, timezone);
21994 previousDate = null;
21999 if (isDefined(attr.min) || attr.ngMin) {
22001 ctrl.$validators.min = function(value) {
22002 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22004 attr.$observe('min', function(val) {
22005 minVal = parseObservedDateValue(val);
22010 if (isDefined(attr.max) || attr.ngMax) {
22012 ctrl.$validators.max = function(value) {
22013 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22015 attr.$observe('max', function(val) {
22016 maxVal = parseObservedDateValue(val);
22021 function isValidDate(value) {
22022 // Invalid Date: getTime() returns NaN
22023 return value && !(value.getTime && value.getTime() !== value.getTime());
22026 function parseObservedDateValue(val) {
22027 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22032 function badInputChecker(scope, element, attr, ctrl) {
22033 var node = element[0];
22034 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22035 if (nativeValidation) {
22036 ctrl.$parsers.push(function(value) {
22037 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22038 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22039 // - also sets validity.badInput (should only be validity.typeMismatch).
22040 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22041 // - can ignore this case as we can still read out the erroneous email...
22042 return validity.badInput && !validity.typeMismatch ? undefined : value;
22047 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22048 badInputChecker(scope, element, attr, ctrl);
22049 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22051 ctrl.$$parserName = 'number';
22052 ctrl.$parsers.push(function(value) {
22053 if (ctrl.$isEmpty(value)) return null;
22054 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22058 ctrl.$formatters.push(function(value) {
22059 if (!ctrl.$isEmpty(value)) {
22060 if (!isNumber(value)) {
22061 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22063 value = value.toString();
22068 if (isDefined(attr.min) || attr.ngMin) {
22070 ctrl.$validators.min = function(value) {
22071 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22074 attr.$observe('min', function(val) {
22075 if (isDefined(val) && !isNumber(val)) {
22076 val = parseFloat(val, 10);
22078 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22079 // TODO(matsko): implement validateLater to reduce number of validations
22084 if (isDefined(attr.max) || attr.ngMax) {
22086 ctrl.$validators.max = function(value) {
22087 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22090 attr.$observe('max', function(val) {
22091 if (isDefined(val) && !isNumber(val)) {
22092 val = parseFloat(val, 10);
22094 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22095 // TODO(matsko): implement validateLater to reduce number of validations
22101 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22102 // Note: no badInputChecker here by purpose as `url` is only a validation
22103 // in browsers, i.e. we can always read out input.value even if it is not valid!
22104 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22105 stringBasedInputType(ctrl);
22107 ctrl.$$parserName = 'url';
22108 ctrl.$validators.url = function(modelValue, viewValue) {
22109 var value = modelValue || viewValue;
22110 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22114 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22115 // Note: no badInputChecker here by purpose as `url` is only a validation
22116 // in browsers, i.e. we can always read out input.value even if it is not valid!
22117 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22118 stringBasedInputType(ctrl);
22120 ctrl.$$parserName = 'email';
22121 ctrl.$validators.email = function(modelValue, viewValue) {
22122 var value = modelValue || viewValue;
22123 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22127 function radioInputType(scope, element, attr, ctrl) {
22128 // make the name unique, if not defined
22129 if (isUndefined(attr.name)) {
22130 element.attr('name', nextUid());
22133 var listener = function(ev) {
22134 if (element[0].checked) {
22135 ctrl.$setViewValue(attr.value, ev && ev.type);
22139 element.on('click', listener);
22141 ctrl.$render = function() {
22142 var value = attr.value;
22143 element[0].checked = (value == ctrl.$viewValue);
22146 attr.$observe('value', ctrl.$render);
22149 function parseConstantExpr($parse, context, name, expression, fallback) {
22151 if (isDefined(expression)) {
22152 parseFn = $parse(expression);
22153 if (!parseFn.constant) {
22154 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22155 '`{1}`.', name, expression);
22157 return parseFn(context);
22162 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22163 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22164 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22166 var listener = function(ev) {
22167 ctrl.$setViewValue(element[0].checked, ev && ev.type);
22170 element.on('click', listener);
22172 ctrl.$render = function() {
22173 element[0].checked = ctrl.$viewValue;
22176 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22177 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22178 // it to a boolean.
22179 ctrl.$isEmpty = function(value) {
22180 return value === false;
22183 ctrl.$formatters.push(function(value) {
22184 return equals(value, trueValue);
22187 ctrl.$parsers.push(function(value) {
22188 return value ? trueValue : falseValue;
22199 * HTML textarea element control with angular data-binding. The data-binding and validation
22200 * properties of this element are exactly the same as those of the
22201 * {@link ng.directive:input input element}.
22203 * @param {string} ngModel Assignable angular expression to data-bind to.
22204 * @param {string=} name Property name of the form under which the control is published.
22205 * @param {string=} required Sets `required` validation error key if the value is not entered.
22206 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22207 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22208 * `required` when you want to data-bind to the `required` attribute.
22209 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22211 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22212 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22214 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22215 * a RegExp found by evaluating the Angular expression given in the attribute value.
22216 * If the expression evaluates to a RegExp object, then this is used directly.
22217 * If the expression evaluates to a string, then it will be converted to a RegExp
22218 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22219 * `new RegExp('^abc$')`.<br />
22220 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22221 * start at the index of the last search's match, thus not taking the whole input value into
22223 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22224 * interaction with the input element.
22225 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22235 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22236 * input state control, and validation.
22237 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22239 * <div class="alert alert-warning">
22240 * **Note:** Not every feature offered is available for all input types.
22241 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22244 * @param {string} ngModel Assignable angular expression to data-bind to.
22245 * @param {string=} name Property name of the form under which the control is published.
22246 * @param {string=} required Sets `required` validation error key if the value is not entered.
22247 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22248 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22250 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22251 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22253 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22254 * a RegExp found by evaluating the Angular expression given in the attribute value.
22255 * If the expression evaluates to a RegExp object, then this is used directly.
22256 * If the expression evaluates to a string, then it will be converted to a RegExp
22257 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22258 * `new RegExp('^abc$')`.<br />
22259 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22260 * start at the index of the last search's match, thus not taking the whole input value into
22262 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22263 * interaction with the input element.
22264 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22265 * This parameter is ignored for input[type=password] controls, which will never trim the
22269 <example name="input-directive" module="inputExample">
22270 <file name="index.html">
22272 angular.module('inputExample', [])
22273 .controller('ExampleController', ['$scope', function($scope) {
22274 $scope.user = {name: 'guest', last: 'visitor'};
22277 <div ng-controller="ExampleController">
22278 <form name="myForm">
22281 <input type="text" name="userName" ng-model="user.name" required>
22284 <span class="error" ng-show="myForm.userName.$error.required">
22289 <input type="text" name="lastName" ng-model="user.last"
22290 ng-minlength="3" ng-maxlength="10">
22293 <span class="error" ng-show="myForm.lastName.$error.minlength">
22295 <span class="error" ng-show="myForm.lastName.$error.maxlength">
22300 <tt>user = {{user}}</tt><br/>
22301 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22302 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22303 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22304 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22305 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22306 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22307 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22308 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22311 <file name="protractor.js" type="protractor">
22312 var user = element(by.exactBinding('user'));
22313 var userNameValid = element(by.binding('myForm.userName.$valid'));
22314 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22315 var lastNameError = element(by.binding('myForm.lastName.$error'));
22316 var formValid = element(by.binding('myForm.$valid'));
22317 var userNameInput = element(by.model('user.name'));
22318 var userLastInput = element(by.model('user.last'));
22320 it('should initialize to model', function() {
22321 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22322 expect(userNameValid.getText()).toContain('true');
22323 expect(formValid.getText()).toContain('true');
22326 it('should be invalid if empty when required', function() {
22327 userNameInput.clear();
22328 userNameInput.sendKeys('');
22330 expect(user.getText()).toContain('{"last":"visitor"}');
22331 expect(userNameValid.getText()).toContain('false');
22332 expect(formValid.getText()).toContain('false');
22335 it('should be valid if empty when min length is set', function() {
22336 userLastInput.clear();
22337 userLastInput.sendKeys('');
22339 expect(user.getText()).toContain('{"name":"guest","last":""}');
22340 expect(lastNameValid.getText()).toContain('true');
22341 expect(formValid.getText()).toContain('true');
22344 it('should be invalid if less than required min length', function() {
22345 userLastInput.clear();
22346 userLastInput.sendKeys('xx');
22348 expect(user.getText()).toContain('{"name":"guest"}');
22349 expect(lastNameValid.getText()).toContain('false');
22350 expect(lastNameError.getText()).toContain('minlength');
22351 expect(formValid.getText()).toContain('false');
22354 it('should be invalid if longer than max length', function() {
22355 userLastInput.clear();
22356 userLastInput.sendKeys('some ridiculously long name');
22358 expect(user.getText()).toContain('{"name":"guest"}');
22359 expect(lastNameValid.getText()).toContain('false');
22360 expect(lastNameError.getText()).toContain('maxlength');
22361 expect(formValid.getText()).toContain('false');
22366 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22367 function($browser, $sniffer, $filter, $parse) {
22370 require: ['?ngModel'],
22372 pre: function(scope, element, attr, ctrls) {
22374 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22375 $browser, $filter, $parse);
22384 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22390 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22391 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22394 * `ngValue` is useful when dynamically generating lists of radio buttons using
22395 * {@link ngRepeat `ngRepeat`}, as shown below.
22397 * Likewise, `ngValue` can be used to generate `<option>` elements for
22398 * the {@link select `select`} element. In that case however, only strings are supported
22399 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22400 * Support for `select` models with non-string values is available via `ngOptions`.
22403 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22404 * of the `input` element
22407 <example name="ngValue-directive" module="valueExample">
22408 <file name="index.html">
22410 angular.module('valueExample', [])
22411 .controller('ExampleController', ['$scope', function($scope) {
22412 $scope.names = ['pizza', 'unicorns', 'robots'];
22413 $scope.my = { favorite: 'unicorns' };
22416 <form ng-controller="ExampleController">
22417 <h2>Which is your favorite?</h2>
22418 <label ng-repeat="name in names" for="{{name}}">
22420 <input type="radio"
22421 ng-model="my.favorite"
22426 <div>You chose {{my.favorite}}</div>
22429 <file name="protractor.js" type="protractor">
22430 var favorite = element(by.binding('my.favorite'));
22432 it('should initialize to model', function() {
22433 expect(favorite.getText()).toContain('unicorns');
22435 it('should bind the values to the inputs', function() {
22436 element.all(by.model('my.favorite')).get(0).click();
22437 expect(favorite.getText()).toContain('pizza');
22442 var ngValueDirective = function() {
22446 compile: function(tpl, tplAttr) {
22447 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22448 return function ngValueConstantLink(scope, elm, attr) {
22449 attr.$set('value', scope.$eval(attr.ngValue));
22452 return function ngValueLink(scope, elm, attr) {
22453 scope.$watch(attr.ngValue, function valueWatchAction(value) {
22454 attr.$set('value', value);
22468 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22469 * with the value of a given expression, and to update the text content when the value of that
22470 * expression changes.
22472 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22473 * `{{ expression }}` which is similar but less verbose.
22475 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22476 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22477 * element attribute, it makes the bindings invisible to the user while the page is loading.
22479 * An alternative solution to this problem would be using the
22480 * {@link ng.directive:ngCloak ngCloak} directive.
22484 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22487 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22488 <example module="bindExample">
22489 <file name="index.html">
22491 angular.module('bindExample', [])
22492 .controller('ExampleController', ['$scope', function($scope) {
22493 $scope.name = 'Whirled';
22496 <div ng-controller="ExampleController">
22497 <label>Enter name: <input type="text" ng-model="name"></label><br>
22498 Hello <span ng-bind="name"></span>!
22501 <file name="protractor.js" type="protractor">
22502 it('should check ng-bind', function() {
22503 var nameInput = element(by.model('name'));
22505 expect(element(by.binding('name')).getText()).toBe('Whirled');
22507 nameInput.sendKeys('world');
22508 expect(element(by.binding('name')).getText()).toBe('world');
22513 var ngBindDirective = ['$compile', function($compile) {
22516 compile: function ngBindCompile(templateElement) {
22517 $compile.$$addBindingClass(templateElement);
22518 return function ngBindLink(scope, element, attr) {
22519 $compile.$$addBindingInfo(element, attr.ngBind);
22520 element = element[0];
22521 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22522 element.textContent = isUndefined(value) ? '' : value;
22532 * @name ngBindTemplate
22535 * The `ngBindTemplate` directive specifies that the element
22536 * text content should be replaced with the interpolation of the template
22537 * in the `ngBindTemplate` attribute.
22538 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22539 * expressions. This directive is needed since some HTML elements
22540 * (such as TITLE and OPTION) cannot contain SPAN elements.
22543 * @param {string} ngBindTemplate template of form
22544 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22547 * Try it here: enter text in text box and watch the greeting change.
22548 <example module="bindExample">
22549 <file name="index.html">
22551 angular.module('bindExample', [])
22552 .controller('ExampleController', ['$scope', function($scope) {
22553 $scope.salutation = 'Hello';
22554 $scope.name = 'World';
22557 <div ng-controller="ExampleController">
22558 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22559 <label>Name: <input type="text" ng-model="name"></label><br>
22560 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22563 <file name="protractor.js" type="protractor">
22564 it('should check ng-bind', function() {
22565 var salutationElem = element(by.binding('salutation'));
22566 var salutationInput = element(by.model('salutation'));
22567 var nameInput = element(by.model('name'));
22569 expect(salutationElem.getText()).toBe('Hello World!');
22571 salutationInput.clear();
22572 salutationInput.sendKeys('Greetings');
22574 nameInput.sendKeys('user');
22576 expect(salutationElem.getText()).toBe('Greetings user!');
22581 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22583 compile: function ngBindTemplateCompile(templateElement) {
22584 $compile.$$addBindingClass(templateElement);
22585 return function ngBindTemplateLink(scope, element, attr) {
22586 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22587 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22588 element = element[0];
22589 attr.$observe('ngBindTemplate', function(value) {
22590 element.textContent = isUndefined(value) ? '' : value;
22603 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22604 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22605 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22606 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22607 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22609 * You may also bypass sanitization for values you know are safe. To do so, bind to
22610 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
22611 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22613 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22614 * will have an exception (instead of an exploit.)
22617 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22621 <example module="bindHtmlExample" deps="angular-sanitize.js">
22622 <file name="index.html">
22623 <div ng-controller="ExampleController">
22624 <p ng-bind-html="myHTML"></p>
22628 <file name="script.js">
22629 angular.module('bindHtmlExample', ['ngSanitize'])
22630 .controller('ExampleController', ['$scope', function($scope) {
22632 'I am an <code>HTML</code>string with ' +
22633 '<a href="#">links!</a> and other <em>stuff</em>';
22637 <file name="protractor.js" type="protractor">
22638 it('should check ng-bind-html', function() {
22639 expect(element(by.binding('myHTML')).getText()).toBe(
22640 'I am an HTMLstring with links! and other stuff');
22645 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22648 compile: function ngBindHtmlCompile(tElement, tAttrs) {
22649 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22650 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22651 return (value || '').toString();
22653 $compile.$$addBindingClass(tElement);
22655 return function ngBindHtmlLink(scope, element, attr) {
22656 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22658 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22659 // we re-evaluate the expr because we want a TrustedValueHolderType
22660 // for $sce, not a string
22661 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22673 * Evaluate the given expression when the user changes the input.
22674 * The expression is evaluated immediately, unlike the JavaScript onchange event
22675 * which only triggers at the end of a change (usually, when the user leaves the
22676 * form element or presses the return key).
22678 * The `ngChange` expression is only evaluated when a change in the input value causes
22679 * a new value to be committed to the model.
22681 * It will not be evaluated:
22682 * * if the value returned from the `$parsers` transformation pipeline has not changed
22683 * * if the input has continued to be invalid since the model will stay `null`
22684 * * if the model is changed programmatically and not by a change to the input value
22687 * Note, this directive requires `ngModel` to be present.
22690 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22694 * <example name="ngChange-directive" module="changeExample">
22695 * <file name="index.html">
22697 * angular.module('changeExample', [])
22698 * .controller('ExampleController', ['$scope', function($scope) {
22699 * $scope.counter = 0;
22700 * $scope.change = function() {
22701 * $scope.counter++;
22705 * <div ng-controller="ExampleController">
22706 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22707 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22708 * <label for="ng-change-example2">Confirmed</label><br />
22709 * <tt>debug = {{confirmed}}</tt><br/>
22710 * <tt>counter = {{counter}}</tt><br/>
22713 * <file name="protractor.js" type="protractor">
22714 * var counter = element(by.binding('counter'));
22715 * var debug = element(by.binding('confirmed'));
22717 * it('should evaluate the expression if changing from view', function() {
22718 * expect(counter.getText()).toContain('0');
22720 * element(by.id('ng-change-example1')).click();
22722 * expect(counter.getText()).toContain('1');
22723 * expect(debug.getText()).toContain('true');
22726 * it('should not evaluate the expression if changing from model', function() {
22727 * element(by.id('ng-change-example2')).click();
22729 * expect(counter.getText()).toContain('0');
22730 * expect(debug.getText()).toContain('true');
22735 var ngChangeDirective = valueFn({
22737 require: 'ngModel',
22738 link: function(scope, element, attr, ctrl) {
22739 ctrl.$viewChangeListeners.push(function() {
22740 scope.$eval(attr.ngChange);
22745 function classDirective(name, selector) {
22746 name = 'ngClass' + name;
22747 return ['$animate', function($animate) {
22750 link: function(scope, element, attr) {
22753 scope.$watch(attr[name], ngClassWatchAction, true);
22755 attr.$observe('class', function(value) {
22756 ngClassWatchAction(scope.$eval(attr[name]));
22760 if (name !== 'ngClass') {
22761 scope.$watch('$index', function($index, old$index) {
22762 // jshint bitwise: false
22763 var mod = $index & 1;
22764 if (mod !== (old$index & 1)) {
22765 var classes = arrayClasses(scope.$eval(attr[name]));
22767 addClasses(classes) :
22768 removeClasses(classes);
22773 function addClasses(classes) {
22774 var newClasses = digestClassCounts(classes, 1);
22775 attr.$addClass(newClasses);
22778 function removeClasses(classes) {
22779 var newClasses = digestClassCounts(classes, -1);
22780 attr.$removeClass(newClasses);
22783 function digestClassCounts(classes, count) {
22784 // Use createMap() to prevent class assumptions involving property
22785 // names in Object.prototype
22786 var classCounts = element.data('$classCounts') || createMap();
22787 var classesToUpdate = [];
22788 forEach(classes, function(className) {
22789 if (count > 0 || classCounts[className]) {
22790 classCounts[className] = (classCounts[className] || 0) + count;
22791 if (classCounts[className] === +(count > 0)) {
22792 classesToUpdate.push(className);
22796 element.data('$classCounts', classCounts);
22797 return classesToUpdate.join(' ');
22800 function updateClasses(oldClasses, newClasses) {
22801 var toAdd = arrayDifference(newClasses, oldClasses);
22802 var toRemove = arrayDifference(oldClasses, newClasses);
22803 toAdd = digestClassCounts(toAdd, 1);
22804 toRemove = digestClassCounts(toRemove, -1);
22805 if (toAdd && toAdd.length) {
22806 $animate.addClass(element, toAdd);
22808 if (toRemove && toRemove.length) {
22809 $animate.removeClass(element, toRemove);
22813 function ngClassWatchAction(newVal) {
22814 if (selector === true || scope.$index % 2 === selector) {
22815 var newClasses = arrayClasses(newVal || []);
22817 addClasses(newClasses);
22818 } else if (!equals(newVal,oldVal)) {
22819 var oldClasses = arrayClasses(oldVal);
22820 updateClasses(oldClasses, newClasses);
22823 oldVal = shallowCopy(newVal);
22828 function arrayDifference(tokens1, tokens2) {
22832 for (var i = 0; i < tokens1.length; i++) {
22833 var token = tokens1[i];
22834 for (var j = 0; j < tokens2.length; j++) {
22835 if (token == tokens2[j]) continue outer;
22837 values.push(token);
22842 function arrayClasses(classVal) {
22844 if (isArray(classVal)) {
22845 forEach(classVal, function(v) {
22846 classes = classes.concat(arrayClasses(v));
22849 } else if (isString(classVal)) {
22850 return classVal.split(' ');
22851 } else if (isObject(classVal)) {
22852 forEach(classVal, function(v, k) {
22854 classes = classes.concat(k.split(' '));
22870 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22871 * an expression that represents all classes to be added.
22873 * The directive operates in three different ways, depending on which of three types the expression
22876 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22879 * 2. If the expression evaluates to an object, then for each key-value pair of the
22880 * object with a truthy value the corresponding key is used as a class name.
22882 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22883 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22884 * to give you more control over what CSS classes appear. See the code below for an example of this.
22887 * The directive won't add duplicate classes if a particular class was already set.
22889 * When the expression changes, the previously added classes are removed and only then are the
22890 * new classes added.
22893 * **add** - happens just before the class is applied to the elements
22895 * **remove** - happens just before the class is removed from the element
22898 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22899 * of the evaluation can be a string representing space delimited class
22900 * names, an array, or a map of class names to boolean values. In the case of a map, the
22901 * names of the properties whose values are truthy will be added as css classes to the
22904 * @example Example that demonstrates basic bindings via ngClass directive.
22906 <file name="index.html">
22907 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22909 <input type="checkbox" ng-model="deleted">
22910 deleted (apply "strike" class)
22913 <input type="checkbox" ng-model="important">
22914 important (apply "bold" class)
22917 <input type="checkbox" ng-model="error">
22918 error (apply "has-error" class)
22921 <p ng-class="style">Using String Syntax</p>
22922 <input type="text" ng-model="style"
22923 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22925 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22926 <input ng-model="style1"
22927 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22928 <input ng-model="style2"
22929 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22930 <input ng-model="style3"
22931 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22933 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22934 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22935 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22937 <file name="style.css">
22939 text-decoration: line-through;
22949 background-color: yellow;
22955 <file name="protractor.js" type="protractor">
22956 var ps = element.all(by.css('p'));
22958 it('should let you toggle the class', function() {
22960 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22961 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22963 element(by.model('important')).click();
22964 expect(ps.first().getAttribute('class')).toMatch(/bold/);
22966 element(by.model('error')).click();
22967 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22970 it('should let you toggle string example', function() {
22971 expect(ps.get(1).getAttribute('class')).toBe('');
22972 element(by.model('style')).clear();
22973 element(by.model('style')).sendKeys('red');
22974 expect(ps.get(1).getAttribute('class')).toBe('red');
22977 it('array example should have 3 classes', function() {
22978 expect(ps.get(2).getAttribute('class')).toBe('');
22979 element(by.model('style1')).sendKeys('bold');
22980 element(by.model('style2')).sendKeys('strike');
22981 element(by.model('style3')).sendKeys('red');
22982 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22985 it('array with map example should have 2 classes', function() {
22986 expect(ps.last().getAttribute('class')).toBe('');
22987 element(by.model('style4')).sendKeys('bold');
22988 element(by.model('warning')).click();
22989 expect(ps.last().getAttribute('class')).toBe('bold orange');
22996 The example below demonstrates how to perform animations using ngClass.
22998 <example module="ngAnimate" deps="angular-animate.js" animations="true">
22999 <file name="index.html">
23000 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23001 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23003 <span class="base-class" ng-class="myVar">Sample Text</span>
23005 <file name="style.css">
23007 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23010 .base-class.my-class {
23015 <file name="protractor.js" type="protractor">
23016 it('should check ng-class', function() {
23017 expect(element(by.css('.base-class')).getAttribute('class')).not.
23018 toMatch(/my-class/);
23020 element(by.id('setbtn')).click();
23022 expect(element(by.css('.base-class')).getAttribute('class')).
23023 toMatch(/my-class/);
23025 element(by.id('clearbtn')).click();
23027 expect(element(by.css('.base-class')).getAttribute('class')).not.
23028 toMatch(/my-class/);
23034 ## ngClass and pre-existing CSS3 Transitions/Animations
23035 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23036 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23037 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23038 to view the step by step details of {@link $animate#addClass $animate.addClass} and
23039 {@link $animate#removeClass $animate.removeClass}.
23041 var ngClassDirective = classDirective('', true);
23049 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23050 * {@link ng.directive:ngClass ngClass}, except they work in
23051 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23053 * This directive can be applied only within the scope of an
23054 * {@link ng.directive:ngRepeat ngRepeat}.
23057 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23058 * of the evaluation can be a string representing space delimited class names or an array.
23062 <file name="index.html">
23063 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23064 <li ng-repeat="name in names">
23065 <span ng-class-odd="'odd'" ng-class-even="'even'">
23071 <file name="style.css">
23079 <file name="protractor.js" type="protractor">
23080 it('should check ng-class-odd and ng-class-even', function() {
23081 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23083 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23089 var ngClassOddDirective = classDirective('Odd', 0);
23093 * @name ngClassEven
23097 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23098 * {@link ng.directive:ngClass ngClass}, except they work in
23099 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23101 * This directive can be applied only within the scope of an
23102 * {@link ng.directive:ngRepeat ngRepeat}.
23105 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23106 * result of the evaluation can be a string representing space delimited class names or an array.
23110 <file name="index.html">
23111 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23112 <li ng-repeat="name in names">
23113 <span ng-class-odd="'odd'" ng-class-even="'even'">
23114 {{name}}
23119 <file name="style.css">
23127 <file name="protractor.js" type="protractor">
23128 it('should check ng-class-odd and ng-class-even', function() {
23129 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23131 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23137 var ngClassEvenDirective = classDirective('Even', 1);
23145 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23146 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23147 * directive to avoid the undesirable flicker effect caused by the html template display.
23149 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23150 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23151 * of the browser view.
23153 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23154 * `angular.min.js`.
23155 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23158 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23159 * display: none !important;
23163 * When this css rule is loaded by the browser, all html elements (including their children) that
23164 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23165 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23166 * the compiled element visible.
23168 * For the best result, the `angular.js` script must be loaded in the head section of the html
23169 * document; alternatively, the css rule above must be included in the external stylesheet of the
23176 <file name="index.html">
23177 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23178 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23180 <file name="protractor.js" type="protractor">
23181 it('should remove the template directive and css class', function() {
23182 expect($('#template1').getAttribute('ng-cloak')).
23184 expect($('#template2').getAttribute('ng-cloak')).
23191 var ngCloakDirective = ngDirective({
23192 compile: function(element, attr) {
23193 attr.$set('ngCloak', undefined);
23194 element.removeClass('ng-cloak');
23200 * @name ngController
23203 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23204 * supports the principles behind the Model-View-Controller design pattern.
23206 * MVC components in angular:
23208 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23209 * are accessed through bindings.
23210 * * View — The template (HTML with data bindings) that is rendered into the View.
23211 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23212 * logic behind the application to decorate the scope with functions and values
23214 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23215 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23216 * again using `ng-controller` in the template itself. This will cause the controller to be attached
23217 * and executed twice.
23222 * @param {expression} ngController Name of a constructor function registered with the current
23223 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23224 * that on the current scope evaluates to a constructor function.
23226 * The controller instance can be published into a scope property by specifying
23227 * `ng-controller="as propertyName"`.
23229 * If the current `$controllerProvider` is configured to use globals (via
23230 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23231 * also be the name of a globally accessible constructor function (not recommended).
23234 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23235 * greeting are methods declared on the controller (see source tab). These methods can
23236 * easily be called from the angular markup. Any changes to the data are automatically reflected
23237 * in the View without the need for a manual update.
23239 * Two different declaration styles are included below:
23241 * * one binds methods and properties directly onto the controller using `this`:
23242 * `ng-controller="SettingsController1 as settings"`
23243 * * one injects `$scope` into the controller:
23244 * `ng-controller="SettingsController2"`
23246 * The second option is more common in the Angular community, and is generally used in boilerplates
23247 * and in this guide. However, there are advantages to binding properties directly to the controller
23248 * and avoiding scope.
23250 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23251 * multiple controllers apply to an element.
23252 * * If you are writing your controllers as classes you have easier access to the properties and
23253 * methods, which will appear on the scope, from inside the controller code.
23254 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23255 * inheritance masking primitives.
23257 * This example demonstrates the `controller as` syntax.
23259 * <example name="ngControllerAs" module="controllerAsExample">
23260 * <file name="index.html">
23261 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23262 * <label>Name: <input type="text" ng-model="settings.name"/></label>
23263 * <button ng-click="settings.greet()">greet</button><br/>
23266 * <li ng-repeat="contact in settings.contacts">
23267 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23268 * <option>phone</option>
23269 * <option>email</option>
23271 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23272 * <button ng-click="settings.clearContact(contact)">clear</button>
23273 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23275 * <li><button ng-click="settings.addContact()">add</button></li>
23279 * <file name="app.js">
23280 * angular.module('controllerAsExample', [])
23281 * .controller('SettingsController1', SettingsController1);
23283 * function SettingsController1() {
23284 * this.name = "John Smith";
23285 * this.contacts = [
23286 * {type: 'phone', value: '408 555 1212'},
23287 * {type: 'email', value: 'john.smith@example.org'} ];
23290 * SettingsController1.prototype.greet = function() {
23291 * alert(this.name);
23294 * SettingsController1.prototype.addContact = function() {
23295 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
23298 * SettingsController1.prototype.removeContact = function(contactToRemove) {
23299 * var index = this.contacts.indexOf(contactToRemove);
23300 * this.contacts.splice(index, 1);
23303 * SettingsController1.prototype.clearContact = function(contact) {
23304 * contact.type = 'phone';
23305 * contact.value = '';
23308 * <file name="protractor.js" type="protractor">
23309 * it('should check controller as', function() {
23310 * var container = element(by.id('ctrl-as-exmpl'));
23311 * expect(container.element(by.model('settings.name'))
23312 * .getAttribute('value')).toBe('John Smith');
23314 * var firstRepeat =
23315 * container.element(by.repeater('contact in settings.contacts').row(0));
23316 * var secondRepeat =
23317 * container.element(by.repeater('contact in settings.contacts').row(1));
23319 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23320 * .toBe('408 555 1212');
23322 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23323 * .toBe('john.smith@example.org');
23325 * firstRepeat.element(by.buttonText('clear')).click();
23327 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23330 * container.element(by.buttonText('add')).click();
23332 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
23333 * .element(by.model('contact.value'))
23334 * .getAttribute('value'))
23335 * .toBe('yourname@example.org');
23340 * This example demonstrates the "attach to `$scope`" style of controller.
23342 * <example name="ngController" module="controllerExample">
23343 * <file name="index.html">
23344 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
23345 * <label>Name: <input type="text" ng-model="name"/></label>
23346 * <button ng-click="greet()">greet</button><br/>
23349 * <li ng-repeat="contact in contacts">
23350 * <select ng-model="contact.type" id="select_{{$index}}">
23351 * <option>phone</option>
23352 * <option>email</option>
23354 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23355 * <button ng-click="clearContact(contact)">clear</button>
23356 * <button ng-click="removeContact(contact)">X</button>
23358 * <li>[ <button ng-click="addContact()">add</button> ]</li>
23362 * <file name="app.js">
23363 * angular.module('controllerExample', [])
23364 * .controller('SettingsController2', ['$scope', SettingsController2]);
23366 * function SettingsController2($scope) {
23367 * $scope.name = "John Smith";
23368 * $scope.contacts = [
23369 * {type:'phone', value:'408 555 1212'},
23370 * {type:'email', value:'john.smith@example.org'} ];
23372 * $scope.greet = function() {
23373 * alert($scope.name);
23376 * $scope.addContact = function() {
23377 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
23380 * $scope.removeContact = function(contactToRemove) {
23381 * var index = $scope.contacts.indexOf(contactToRemove);
23382 * $scope.contacts.splice(index, 1);
23385 * $scope.clearContact = function(contact) {
23386 * contact.type = 'phone';
23387 * contact.value = '';
23391 * <file name="protractor.js" type="protractor">
23392 * it('should check controller', function() {
23393 * var container = element(by.id('ctrl-exmpl'));
23395 * expect(container.element(by.model('name'))
23396 * .getAttribute('value')).toBe('John Smith');
23398 * var firstRepeat =
23399 * container.element(by.repeater('contact in contacts').row(0));
23400 * var secondRepeat =
23401 * container.element(by.repeater('contact in contacts').row(1));
23403 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23404 * .toBe('408 555 1212');
23405 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23406 * .toBe('john.smith@example.org');
23408 * firstRepeat.element(by.buttonText('clear')).click();
23410 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23413 * container.element(by.buttonText('add')).click();
23415 * expect(container.element(by.repeater('contact in contacts').row(2))
23416 * .element(by.model('contact.value'))
23417 * .getAttribute('value'))
23418 * .toBe('yourname@example.org');
23424 var ngControllerDirective = [function() {
23440 * Angular has some features that can break certain
23441 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23443 * If you intend to implement these rules then you must tell Angular not to use these features.
23445 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23448 * The following rules affect Angular:
23450 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23451 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23452 * increase in the speed of evaluating Angular expressions.
23454 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23455 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23456 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23457 * `angular-csp.css` in your HTML manually.
23459 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23460 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23461 * however, triggers a CSP error to be logged in the console:
23464 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23465 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23466 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23469 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23470 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23471 * the `angular.js` file.
23473 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23475 * You can specify which of the CSP related Angular features should be deactivated by providing
23476 * a value for the `ng-csp` attribute. The options are as follows:
23478 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23480 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23482 * You can use these values in the following combinations:
23485 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23486 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23489 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23490 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23493 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23494 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23496 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23497 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23499 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23500 * styles nor use eval, which is the same as an empty: ng-csp.
23501 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23504 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23507 <html ng-app ng-csp>
23513 // Note: the suffix `.csp` in the example name triggers
23514 // csp mode in our http server!
23515 <example name="example.csp" module="cspExample" ng-csp="true">
23516 <file name="index.html">
23517 <div ng-controller="MainController as ctrl">
23519 <button ng-click="ctrl.inc()" id="inc">Increment</button>
23520 <span id="counter">
23526 <button ng-click="ctrl.evil()" id="evil">Evil</button>
23527 <span id="evilError">
23533 <file name="script.js">
23534 angular.module('cspExample', [])
23535 .controller('MainController', function() {
23537 this.inc = function() {
23540 this.evil = function() {
23541 // jshint evil:true
23545 this.evilError = e.message;
23550 <file name="protractor.js" type="protractor">
23551 var util, webdriver;
23553 var incBtn = element(by.id('inc'));
23554 var counter = element(by.id('counter'));
23555 var evilBtn = element(by.id('evil'));
23556 var evilError = element(by.id('evilError'));
23558 function getAndClearSevereErrors() {
23559 return browser.manage().logs().get('browser').then(function(browserLog) {
23560 return browserLog.filter(function(logEntry) {
23561 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23566 function clearErrors() {
23567 getAndClearSevereErrors();
23570 function expectNoErrors() {
23571 getAndClearSevereErrors().then(function(filteredLog) {
23572 expect(filteredLog.length).toEqual(0);
23573 if (filteredLog.length) {
23574 console.log('browser console errors: ' + util.inspect(filteredLog));
23579 function expectError(regex) {
23580 getAndClearSevereErrors().then(function(filteredLog) {
23582 filteredLog.forEach(function(log) {
23583 if (log.message.match(regex)) {
23588 throw new Error('expected an error that matches ' + regex);
23593 beforeEach(function() {
23594 util = require('util');
23595 webdriver = require('protractor/node_modules/selenium-webdriver');
23598 // For now, we only test on Chrome,
23599 // as Safari does not load the page with Protractor's injected scripts,
23600 // and Firefox webdriver always disables content security policy (#6358)
23601 if (browser.params.browser !== 'chrome') {
23605 it('should not report errors when the page is loaded', function() {
23606 // clear errors so we are not dependent on previous tests
23608 // Need to reload the page as the page is already loaded when
23610 browser.driver.getCurrentUrl().then(function(url) {
23616 it('should evaluate expressions', function() {
23617 expect(counter.getText()).toEqual('0');
23619 expect(counter.getText()).toEqual('1');
23623 it('should throw and report an error when using "eval"', function() {
23625 expect(evilError.getText()).toMatch(/Content Security Policy/);
23626 expectError(/Content Security Policy/);
23632 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23633 // bootstrap the system (before $parse is instantiated), for this reason we just have
23634 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23641 * The ngClick directive allows you to specify custom behavior when
23642 * an element is clicked.
23646 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23647 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23651 <file name="index.html">
23652 <button ng-click="count = count + 1" ng-init="count=0">
23659 <file name="protractor.js" type="protractor">
23660 it('should check ng-click', function() {
23661 expect(element(by.binding('count')).getText()).toMatch('0');
23662 element(by.css('button')).click();
23663 expect(element(by.binding('count')).getText()).toMatch('1');
23669 * A collection of directives that allows creation of custom event handlers that are defined as
23670 * angular expressions and are compiled and executed within the current scope.
23672 var ngEventDirectives = {};
23674 // For events that might fire synchronously during DOM manipulation
23675 // we need to execute their event handlers asynchronously using $evalAsync,
23676 // so that they are not executed in an inconsistent state.
23677 var forceAsyncEvents = {
23682 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23683 function(eventName) {
23684 var directiveName = directiveNormalize('ng-' + eventName);
23685 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23688 compile: function($element, attr) {
23689 // We expose the powerful $event object on the scope that provides access to the Window,
23690 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
23691 // checks at the cost of speed since event handler expressions are not executed as
23692 // frequently as regular change detection.
23693 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23694 return function ngEventHandler(scope, element) {
23695 element.on(eventName, function(event) {
23696 var callback = function() {
23697 fn(scope, {$event:event});
23699 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23700 scope.$evalAsync(callback);
23702 scope.$apply(callback);
23717 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23721 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23722 * a dblclick. (The Event object is available as `$event`)
23726 <file name="index.html">
23727 <button ng-dblclick="count = count + 1" ng-init="count=0">
23728 Increment (on double click)
23738 * @name ngMousedown
23741 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23745 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23746 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23750 <file name="index.html">
23751 <button ng-mousedown="count = count + 1" ng-init="count=0">
23752 Increment (on mouse down)
23765 * Specify custom behavior on mouseup event.
23769 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23770 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23774 <file name="index.html">
23775 <button ng-mouseup="count = count + 1" ng-init="count=0">
23776 Increment (on mouse up)
23785 * @name ngMouseover
23788 * Specify custom behavior on mouseover event.
23792 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23793 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23797 <file name="index.html">
23798 <button ng-mouseover="count = count + 1" ng-init="count=0">
23799 Increment (when mouse is over)
23809 * @name ngMouseenter
23812 * Specify custom behavior on mouseenter event.
23816 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23817 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23821 <file name="index.html">
23822 <button ng-mouseenter="count = count + 1" ng-init="count=0">
23823 Increment (when mouse enters)
23833 * @name ngMouseleave
23836 * Specify custom behavior on mouseleave event.
23840 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23841 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23845 <file name="index.html">
23846 <button ng-mouseleave="count = count + 1" ng-init="count=0">
23847 Increment (when mouse leaves)
23857 * @name ngMousemove
23860 * Specify custom behavior on mousemove event.
23864 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23865 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23869 <file name="index.html">
23870 <button ng-mousemove="count = count + 1" ng-init="count=0">
23871 Increment (when mouse moves)
23884 * Specify custom behavior on keydown event.
23888 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23889 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23893 <file name="index.html">
23894 <input ng-keydown="count = count + 1" ng-init="count=0">
23895 key down count: {{count}}
23906 * Specify custom behavior on keyup event.
23910 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23911 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23915 <file name="index.html">
23916 <p>Typing in the input box below updates the key count</p>
23917 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23919 <p>Typing in the input box below updates the keycode</p>
23920 <input ng-keyup="event=$event">
23921 <p>event keyCode: {{ event.keyCode }}</p>
23922 <p>event altKey: {{ event.altKey }}</p>
23933 * Specify custom behavior on keypress event.
23936 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23937 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23938 * and can be interrogated for keyCode, altKey, etc.)
23942 <file name="index.html">
23943 <input ng-keypress="count = count + 1" ng-init="count=0">
23944 key press count: {{count}}
23955 * Enables binding angular expressions to onsubmit events.
23957 * Additionally it prevents the default action (which for form means sending the request to the
23958 * server and reloading the current page), but only if the form does not contain `action`,
23959 * `data-action`, or `x-action` attributes.
23961 * <div class="alert alert-warning">
23962 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23963 * `ngSubmit` handlers together. See the
23964 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23965 * for a detailed discussion of when `ngSubmit` may be triggered.
23970 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23971 * ({@link guide/expression#-event- Event object is available as `$event`})
23974 <example module="submitExample">
23975 <file name="index.html">
23977 angular.module('submitExample', [])
23978 .controller('ExampleController', ['$scope', function($scope) {
23980 $scope.text = 'hello';
23981 $scope.submit = function() {
23983 $scope.list.push(this.text);
23989 <form ng-submit="submit()" ng-controller="ExampleController">
23990 Enter text and hit enter:
23991 <input type="text" ng-model="text" name="text" />
23992 <input type="submit" id="submit" value="Submit" />
23993 <pre>list={{list}}</pre>
23996 <file name="protractor.js" type="protractor">
23997 it('should check ng-submit', function() {
23998 expect(element(by.binding('list')).getText()).toBe('list=[]');
23999 element(by.css('#submit')).click();
24000 expect(element(by.binding('list')).getText()).toContain('hello');
24001 expect(element(by.model('text')).getAttribute('value')).toBe('');
24003 it('should ignore empty strings', function() {
24004 expect(element(by.binding('list')).getText()).toBe('list=[]');
24005 element(by.css('#submit')).click();
24006 element(by.css('#submit')).click();
24007 expect(element(by.binding('list')).getText()).toContain('hello');
24018 * Specify custom behavior on focus event.
24020 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24021 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24022 * during an `$apply` to ensure a consistent state.
24024 * @element window, input, select, textarea, a
24026 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24027 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24030 * See {@link ng.directive:ngClick ngClick}
24038 * Specify custom behavior on blur event.
24040 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24041 * an element has lost focus.
24043 * Note: As the `blur` event is executed synchronously also during DOM manipulations
24044 * (e.g. removing a focussed input),
24045 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24046 * during an `$apply` to ensure a consistent state.
24048 * @element window, input, select, textarea, a
24050 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24051 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24054 * See {@link ng.directive:ngClick ngClick}
24062 * Specify custom behavior on copy event.
24064 * @element window, input, select, textarea, a
24066 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24067 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24071 <file name="index.html">
24072 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24083 * Specify custom behavior on cut event.
24085 * @element window, input, select, textarea, a
24087 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24088 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24092 <file name="index.html">
24093 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24104 * Specify custom behavior on paste event.
24106 * @element window, input, select, textarea, a
24108 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24109 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24113 <file name="index.html">
24114 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24127 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24128 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24129 * value then the element is removed from the DOM, otherwise a clone of the
24130 * element is reinserted into the DOM.
24132 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24133 * element in the DOM rather than changing its visibility via the `display` css property. A common
24134 * case when this difference is significant is when using css selectors that rely on an element's
24135 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24137 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24138 * is created when the element is restored. The scope created within `ngIf` inherits from
24139 * its parent scope using
24140 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24141 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24142 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24143 * variable within the child scope will override (hide) the value in the parent scope.
24145 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24146 * is if an element's class attribute is directly modified after it's compiled, using something like
24147 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24148 * the added class will be lost because the original compiled state is used to regenerate the element.
24150 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24151 * and `leave` effects.
24154 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24155 * leave - happens just before the `ngIf` contents are removed from the DOM
24160 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24161 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
24162 * element is added to the DOM tree.
24165 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24166 <file name="index.html">
24167 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24169 <span ng-if="checked" class="animate-if">
24170 This is removed when the checkbox is unchecked.
24173 <file name="animations.css">
24176 border:1px solid black;
24180 .animate-if.ng-enter, .animate-if.ng-leave {
24181 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24184 .animate-if.ng-enter,
24185 .animate-if.ng-leave.ng-leave-active {
24189 .animate-if.ng-leave,
24190 .animate-if.ng-enter.ng-enter-active {
24196 var ngIfDirective = ['$animate', function($animate) {
24198 multiElement: true,
24199 transclude: 'element',
24204 link: function($scope, $element, $attr, ctrl, $transclude) {
24205 var block, childScope, previousElements;
24206 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24210 $transclude(function(clone, newScope) {
24211 childScope = newScope;
24212 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24213 // Note: We only need the first/last node of the cloned nodes.
24214 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24215 // by a directive with templateUrl when its template arrives.
24219 $animate.enter(clone, $element.parent(), $element);
24223 if (previousElements) {
24224 previousElements.remove();
24225 previousElements = null;
24228 childScope.$destroy();
24232 previousElements = getBlockNodes(block.clone);
24233 $animate.leave(previousElements).then(function() {
24234 previousElements = null;
24250 * Fetches, compiles and includes an external HTML fragment.
24252 * By default, the template URL is restricted to the same domain and protocol as the
24253 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24254 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24255 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24256 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24257 * ng.$sce Strict Contextual Escaping}.
24259 * In addition, the browser's
24260 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24261 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24262 * policy may further restrict whether the template is successfully loaded.
24263 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24264 * access on some browsers.
24267 * enter - animation is used to bring new content into the browser.
24268 * leave - animation is used to animate existing content away.
24270 * The enter and leave animation occur concurrently.
24275 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24276 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24277 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24278 * <div class="alert alert-warning">
24279 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
24280 * a function with the name on the window element, which will usually throw a
24281 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
24282 * different form that {@link guide/directive#normalization matches} `onload`.
24285 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24286 * $anchorScroll} to scroll the viewport after the content is loaded.
24288 * - If the attribute is not set, disable scrolling.
24289 * - If the attribute is set without value, enable scrolling.
24290 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
24293 <example module="includeExample" deps="angular-animate.js" animations="true">
24294 <file name="index.html">
24295 <div ng-controller="ExampleController">
24296 <select ng-model="template" ng-options="t.name for t in templates">
24297 <option value="">(blank)</option>
24299 url of the template: <code>{{template.url}}</code>
24301 <div class="slide-animate-container">
24302 <div class="slide-animate" ng-include="template.url"></div>
24306 <file name="script.js">
24307 angular.module('includeExample', ['ngAnimate'])
24308 .controller('ExampleController', ['$scope', function($scope) {
24310 [ { name: 'template1.html', url: 'template1.html'},
24311 { name: 'template2.html', url: 'template2.html'} ];
24312 $scope.template = $scope.templates[0];
24315 <file name="template1.html">
24316 Content of template1.html
24318 <file name="template2.html">
24319 Content of template2.html
24321 <file name="animations.css">
24322 .slide-animate-container {
24325 border:1px solid black;
24334 .slide-animate.ng-enter, .slide-animate.ng-leave {
24335 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24346 .slide-animate.ng-enter {
24349 .slide-animate.ng-enter.ng-enter-active {
24353 .slide-animate.ng-leave {
24356 .slide-animate.ng-leave.ng-leave-active {
24360 <file name="protractor.js" type="protractor">
24361 var templateSelect = element(by.model('template'));
24362 var includeElem = element(by.css('[ng-include]'));
24364 it('should load template1.html', function() {
24365 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24368 it('should load template2.html', function() {
24369 if (browser.params.browser == 'firefox') {
24370 // Firefox can't handle using selects
24371 // See https://github.com/angular/protractor/issues/480
24374 templateSelect.click();
24375 templateSelect.all(by.css('option')).get(2).click();
24376 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24379 it('should change to blank', function() {
24380 if (browser.params.browser == 'firefox') {
24381 // Firefox can't handle using selects
24384 templateSelect.click();
24385 templateSelect.all(by.css('option')).get(0).click();
24386 expect(includeElem.isPresent()).toBe(false);
24395 * @name ngInclude#$includeContentRequested
24396 * @eventType emit on the scope ngInclude was declared in
24398 * Emitted every time the ngInclude content is requested.
24400 * @param {Object} angularEvent Synthetic event object.
24401 * @param {String} src URL of content to load.
24407 * @name ngInclude#$includeContentLoaded
24408 * @eventType emit on the current ngInclude scope
24410 * Emitted every time the ngInclude content is reloaded.
24412 * @param {Object} angularEvent Synthetic event object.
24413 * @param {String} src URL of content to load.
24419 * @name ngInclude#$includeContentError
24420 * @eventType emit on the scope ngInclude was declared in
24422 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24424 * @param {Object} angularEvent Synthetic event object.
24425 * @param {String} src URL of content to load.
24427 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24428 function($templateRequest, $anchorScroll, $animate) {
24433 transclude: 'element',
24434 controller: angular.noop,
24435 compile: function(element, attr) {
24436 var srcExp = attr.ngInclude || attr.src,
24437 onloadExp = attr.onload || '',
24438 autoScrollExp = attr.autoscroll;
24440 return function(scope, $element, $attr, ctrl, $transclude) {
24441 var changeCounter = 0,
24446 var cleanupLastIncludeContent = function() {
24447 if (previousElement) {
24448 previousElement.remove();
24449 previousElement = null;
24451 if (currentScope) {
24452 currentScope.$destroy();
24453 currentScope = null;
24455 if (currentElement) {
24456 $animate.leave(currentElement).then(function() {
24457 previousElement = null;
24459 previousElement = currentElement;
24460 currentElement = null;
24464 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24465 var afterAnimation = function() {
24466 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24470 var thisChangeId = ++changeCounter;
24473 //set the 2nd param to true to ignore the template request error so that the inner
24474 //contents and scope can be cleaned up.
24475 $templateRequest(src, true).then(function(response) {
24476 if (thisChangeId !== changeCounter) return;
24477 var newScope = scope.$new();
24478 ctrl.template = response;
24480 // Note: This will also link all children of ng-include that were contained in the original
24481 // html. If that content contains controllers, ... they could pollute/change the scope.
24482 // However, using ng-include on an element with additional content does not make sense...
24483 // Note: We can't remove them in the cloneAttchFn of $transclude as that
24484 // function is called before linking the content, which would apply child
24485 // directives to non existing elements.
24486 var clone = $transclude(newScope, function(clone) {
24487 cleanupLastIncludeContent();
24488 $animate.enter(clone, null, $element).then(afterAnimation);
24491 currentScope = newScope;
24492 currentElement = clone;
24494 currentScope.$emit('$includeContentLoaded', src);
24495 scope.$eval(onloadExp);
24497 if (thisChangeId === changeCounter) {
24498 cleanupLastIncludeContent();
24499 scope.$emit('$includeContentError', src);
24502 scope.$emit('$includeContentRequested', src);
24504 cleanupLastIncludeContent();
24505 ctrl.template = null;
24513 // This directive is called during the $transclude call of the first `ngInclude` directive.
24514 // It will replace and compile the content of the element with the loaded template.
24515 // We need this directive so that the element content is already filled when
24516 // the link function of another directive on the same element as ngInclude
24518 var ngIncludeFillContentDirective = ['$compile',
24519 function($compile) {
24523 require: 'ngInclude',
24524 link: function(scope, $element, $attr, ctrl) {
24525 if (/SVG/.test($element[0].toString())) {
24526 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24527 // support innerHTML, so detect this here and try to generate the contents
24530 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24531 function namespaceAdaptedClone(clone) {
24532 $element.append(clone);
24533 }, {futureParentElement: $element});
24537 $element.html(ctrl.template);
24538 $compile($element.contents())(scope);
24549 * The `ngInit` directive allows you to evaluate an expression in the
24552 * <div class="alert alert-danger">
24553 * This directive can be abused to add unnecessary amounts of logic into your templates.
24554 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24555 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24556 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24557 * rather than `ngInit` to initialize values on a scope.
24560 * <div class="alert alert-warning">
24561 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24562 * sure you have parentheses to ensure correct operator precedence:
24563 * <pre class="prettyprint">
24564 * `<div ng-init="test1 = ($index | toString)"></div>`
24571 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24574 <example module="initExample">
24575 <file name="index.html">
24577 angular.module('initExample', [])
24578 .controller('ExampleController', ['$scope', function($scope) {
24579 $scope.list = [['a', 'b'], ['c', 'd']];
24582 <div ng-controller="ExampleController">
24583 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24584 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24585 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24590 <file name="protractor.js" type="protractor">
24591 it('should alias index positions', function() {
24592 var elements = element.all(by.css('.example-init'));
24593 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24594 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24595 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24596 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24601 var ngInitDirective = ngDirective({
24603 compile: function() {
24605 pre: function(scope, element, attrs) {
24606 scope.$eval(attrs.ngInit);
24617 * Text input that converts between a delimited string and an array of strings. The default
24618 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24619 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24621 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24622 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24623 * list item is respected. This implies that the user of the directive is responsible for
24624 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24625 * tab or newline character.
24626 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24627 * when joining the list items back together) and whitespace around each list item is stripped
24628 * before it is added to the model.
24630 * ### Example with Validation
24632 * <example name="ngList-directive" module="listExample">
24633 * <file name="app.js">
24634 * angular.module('listExample', [])
24635 * .controller('ExampleController', ['$scope', function($scope) {
24636 * $scope.names = ['morpheus', 'neo', 'trinity'];
24639 * <file name="index.html">
24640 * <form name="myForm" ng-controller="ExampleController">
24641 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24642 * <span role="alert">
24643 * <span class="error" ng-show="myForm.namesInput.$error.required">
24647 * <tt>names = {{names}}</tt><br/>
24648 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24649 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24650 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24651 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24654 * <file name="protractor.js" type="protractor">
24655 * var listInput = element(by.model('names'));
24656 * var names = element(by.exactBinding('names'));
24657 * var valid = element(by.binding('myForm.namesInput.$valid'));
24658 * var error = element(by.css('span.error'));
24660 * it('should initialize to model', function() {
24661 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24662 * expect(valid.getText()).toContain('true');
24663 * expect(error.getCssValue('display')).toBe('none');
24666 * it('should be invalid if empty', function() {
24667 * listInput.clear();
24668 * listInput.sendKeys('');
24670 * expect(names.getText()).toContain('');
24671 * expect(valid.getText()).toContain('false');
24672 * expect(error.getCssValue('display')).not.toBe('none');
24677 * ### Example - splitting on newline
24678 * <example name="ngList-directive-newlines">
24679 * <file name="index.html">
24680 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
24681 * <pre>{{ list | json }}</pre>
24683 * <file name="protractor.js" type="protractor">
24684 * it("should split the text by newlines", function() {
24685 * var listInput = element(by.model('list'));
24686 * var output = element(by.binding('list | json'));
24687 * listInput.sendKeys('abc\ndef\nghi');
24688 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
24694 * @param {string=} ngList optional delimiter that should be used to split the value.
24696 var ngListDirective = function() {
24700 require: 'ngModel',
24701 link: function(scope, element, attr, ctrl) {
24702 // We want to control whitespace trimming so we use this convoluted approach
24703 // to access the ngList attribute, which doesn't pre-trim the attribute
24704 var ngList = element.attr(attr.$attr.ngList) || ', ';
24705 var trimValues = attr.ngTrim !== 'false';
24706 var separator = trimValues ? trim(ngList) : ngList;
24708 var parse = function(viewValue) {
24709 // If the viewValue is invalid (say required but empty) it will be `undefined`
24710 if (isUndefined(viewValue)) return;
24715 forEach(viewValue.split(separator), function(value) {
24716 if (value) list.push(trimValues ? trim(value) : value);
24723 ctrl.$parsers.push(parse);
24724 ctrl.$formatters.push(function(value) {
24725 if (isArray(value)) {
24726 return value.join(ngList);
24732 // Override the standard $isEmpty because an empty array means the input is empty.
24733 ctrl.$isEmpty = function(value) {
24734 return !value || !value.length;
24740 /* global VALID_CLASS: true,
24741 INVALID_CLASS: true,
24742 PRISTINE_CLASS: true,
24744 UNTOUCHED_CLASS: true,
24745 TOUCHED_CLASS: true,
24748 var VALID_CLASS = 'ng-valid',
24749 INVALID_CLASS = 'ng-invalid',
24750 PRISTINE_CLASS = 'ng-pristine',
24751 DIRTY_CLASS = 'ng-dirty',
24752 UNTOUCHED_CLASS = 'ng-untouched',
24753 TOUCHED_CLASS = 'ng-touched',
24754 PENDING_CLASS = 'ng-pending';
24756 var ngModelMinErr = minErr('ngModel');
24760 * @name ngModel.NgModelController
24762 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24763 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24765 * @property {*} $modelValue The value in the model that the control is bound to.
24766 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24767 the control reads value from the DOM. The functions are called in array order, each passing
24768 its return value through to the next. The last return value is forwarded to the
24769 {@link ngModel.NgModelController#$validators `$validators`} collection.
24771 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24774 Returning `undefined` from a parser means a parse error occurred. In that case,
24775 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24776 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24777 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24780 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24781 the model value changes. The functions are called in reverse array order, each passing the value through to the
24782 next. The last return value is used as the actual DOM value.
24783 Used to format / convert values for display in the control.
24785 * function formatter(value) {
24787 * return value.toUpperCase();
24790 * ngModel.$formatters.push(formatter);
24793 * @property {Object.<string, function>} $validators A collection of validators that are applied
24794 * whenever the model value changes. The key value within the object refers to the name of the
24795 * validator while the function refers to the validation operation. The validation operation is
24796 * provided with the model value as an argument and must return a true or false value depending
24797 * on the response of that validation.
24800 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24801 * var value = modelValue || viewValue;
24802 * return /[0-9]+/.test(value) &&
24803 * /[a-z]+/.test(value) &&
24804 * /[A-Z]+/.test(value) &&
24805 * /\W+/.test(value);
24809 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24810 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24811 * is expected to return a promise when it is run during the model validation process. Once the promise
24812 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
24813 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24814 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24815 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24816 * will only run once all synchronous validators have passed.
24818 * Please note that if $http is used then it is important that the server returns a success HTTP response code
24819 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24822 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24823 * var value = modelValue || viewValue;
24825 * // Lookup user by username
24826 * return $http.get('/api/users/' + value).
24827 * then(function resolved() {
24828 * //username exists, this means validation fails
24829 * return $q.reject('exists');
24830 * }, function rejected() {
24831 * //username does not exist, therefore this validation passes
24837 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24838 * view value has changed. It is called with no arguments, and its return value is ignored.
24839 * This can be used in place of additional $watches against the model value.
24841 * @property {Object} $error An object hash with all failing validator ids as keys.
24842 * @property {Object} $pending An object hash with all pending validator ids as keys.
24844 * @property {boolean} $untouched True if control has not lost focus yet.
24845 * @property {boolean} $touched True if control has lost focus.
24846 * @property {boolean} $pristine True if user has not interacted with the control yet.
24847 * @property {boolean} $dirty True if user has already interacted with the control.
24848 * @property {boolean} $valid True if there is no error.
24849 * @property {boolean} $invalid True if at least one error on the control.
24850 * @property {string} $name The name attribute of the control.
24854 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24855 * The controller contains services for data-binding, validation, CSS updates, and value formatting
24856 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24857 * listening to DOM events.
24858 * Such DOM related logic should be provided by other directives which make use of
24859 * `NgModelController` for data-binding to control elements.
24860 * Angular provides this DOM logic for most {@link input `input`} elements.
24861 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24862 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24865 * ### Custom Control Example
24866 * This example shows how to use `NgModelController` with a custom control to achieve
24867 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24868 * collaborate together to achieve the desired result.
24870 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24871 * contents be edited in place by the user.
24873 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24874 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24875 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24876 * that content using the `$sce` service.
24878 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24879 <file name="style.css">
24880 [contenteditable] {
24881 border: 1px solid black;
24882 background-color: white;
24887 border: 1px solid red;
24891 <file name="script.js">
24892 angular.module('customControl', ['ngSanitize']).
24893 directive('contenteditable', ['$sce', function($sce) {
24895 restrict: 'A', // only activate on element attribute
24896 require: '?ngModel', // get a hold of NgModelController
24897 link: function(scope, element, attrs, ngModel) {
24898 if (!ngModel) return; // do nothing if no ng-model
24900 // Specify how UI should be updated
24901 ngModel.$render = function() {
24902 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24905 // Listen for change events to enable binding
24906 element.on('blur keyup change', function() {
24907 scope.$evalAsync(read);
24909 read(); // initialize
24911 // Write data to the model
24913 var html = element.html();
24914 // When we clear the content editable the browser leaves a <br> behind
24915 // If strip-br attribute is provided then we strip this out
24916 if ( attrs.stripBr && html == '<br>' ) {
24919 ngModel.$setViewValue(html);
24925 <file name="index.html">
24926 <form name="myForm">
24927 <div contenteditable
24928 name="myWidget" ng-model="userContent"
24930 required>Change me!</div>
24931 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24933 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24936 <file name="protractor.js" type="protractor">
24937 it('should data-bind and become invalid', function() {
24938 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24939 // SafariDriver can't handle contenteditable
24940 // and Firefox driver can't clear contenteditables very well
24943 var contentEditable = element(by.css('[contenteditable]'));
24944 var content = 'Change me!';
24946 expect(contentEditable.getText()).toEqual(content);
24948 contentEditable.clear();
24949 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24950 expect(contentEditable.getText()).toEqual('');
24951 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24958 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24959 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24960 this.$viewValue = Number.NaN;
24961 this.$modelValue = Number.NaN;
24962 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24963 this.$validators = {};
24964 this.$asyncValidators = {};
24965 this.$parsers = [];
24966 this.$formatters = [];
24967 this.$viewChangeListeners = [];
24968 this.$untouched = true;
24969 this.$touched = false;
24970 this.$pristine = true;
24971 this.$dirty = false;
24972 this.$valid = true;
24973 this.$invalid = false;
24974 this.$error = {}; // keep invalid keys here
24975 this.$$success = {}; // keep valid keys here
24976 this.$pending = undefined; // keep pending keys here
24977 this.$name = $interpolate($attr.name || '', false)($scope);
24978 this.$$parentForm = nullFormCtrl;
24980 var parsedNgModel = $parse($attr.ngModel),
24981 parsedNgModelAssign = parsedNgModel.assign,
24982 ngModelGet = parsedNgModel,
24983 ngModelSet = parsedNgModelAssign,
24984 pendingDebounce = null,
24988 this.$$setOptions = function(options) {
24989 ctrl.$options = options;
24990 if (options && options.getterSetter) {
24991 var invokeModelGetter = $parse($attr.ngModel + '()'),
24992 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24994 ngModelGet = function($scope) {
24995 var modelValue = parsedNgModel($scope);
24996 if (isFunction(modelValue)) {
24997 modelValue = invokeModelGetter($scope);
25001 ngModelSet = function($scope, newValue) {
25002 if (isFunction(parsedNgModel($scope))) {
25003 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25005 parsedNgModelAssign($scope, ctrl.$modelValue);
25008 } else if (!parsedNgModel.assign) {
25009 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25010 $attr.ngModel, startingTag($element));
25016 * @name ngModel.NgModelController#$render
25019 * Called when the view needs to be updated. It is expected that the user of the ng-model
25020 * directive will implement this method.
25022 * The `$render()` method is invoked in the following situations:
25024 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
25025 * committed value then `$render()` is called to update the input control.
25026 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25027 * the `$viewValue` are different from last time.
25029 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25030 * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25031 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25032 * invoked if you only change a property on the objects.
25034 this.$render = noop;
25038 * @name ngModel.NgModelController#$isEmpty
25041 * This is called when we need to determine if the value of an input is empty.
25043 * For instance, the required directive does this to work out if the input has data or not.
25045 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25047 * You can override this for input directives whose concept of being empty is different from the
25048 * default. The `checkboxInputType` directive does this because in its case a value of `false`
25051 * @param {*} value The value of the input to check for emptiness.
25052 * @returns {boolean} True if `value` is "empty".
25054 this.$isEmpty = function(value) {
25055 return isUndefined(value) || value === '' || value === null || value !== value;
25058 var currentValidationRunId = 0;
25062 * @name ngModel.NgModelController#$setValidity
25065 * Change the validity state, and notify the form.
25067 * This method can be called within $parsers/$formatters or a custom validation implementation.
25068 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25069 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25071 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25072 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25073 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25074 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
25075 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25076 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
25077 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25078 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25079 * Skipped is used by Angular when validators do not run because of parse errors and
25080 * when `$asyncValidators` do not run because any of the `$validators` failed.
25082 addSetValidityMethod({
25084 $element: $element,
25085 set: function(object, property) {
25086 object[property] = true;
25088 unset: function(object, property) {
25089 delete object[property];
25096 * @name ngModel.NgModelController#$setPristine
25099 * Sets the control to its pristine state.
25101 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25102 * state (`ng-pristine` class). A model is considered to be pristine when the control
25103 * has not been changed from when first compiled.
25105 this.$setPristine = function() {
25106 ctrl.$dirty = false;
25107 ctrl.$pristine = true;
25108 $animate.removeClass($element, DIRTY_CLASS);
25109 $animate.addClass($element, PRISTINE_CLASS);
25114 * @name ngModel.NgModelController#$setDirty
25117 * Sets the control to its dirty state.
25119 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25120 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25121 * from when first compiled.
25123 this.$setDirty = function() {
25124 ctrl.$dirty = true;
25125 ctrl.$pristine = false;
25126 $animate.removeClass($element, PRISTINE_CLASS);
25127 $animate.addClass($element, DIRTY_CLASS);
25128 ctrl.$$parentForm.$setDirty();
25133 * @name ngModel.NgModelController#$setUntouched
25136 * Sets the control to its untouched state.
25138 * This method can be called to remove the `ng-touched` class and set the control to its
25139 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25140 * by default, however this function can be used to restore that state if the model has
25141 * already been touched by the user.
25143 this.$setUntouched = function() {
25144 ctrl.$touched = false;
25145 ctrl.$untouched = true;
25146 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25151 * @name ngModel.NgModelController#$setTouched
25154 * Sets the control to its touched state.
25156 * This method can be called to remove the `ng-untouched` class and set the control to its
25157 * touched state (`ng-touched` class). A model is considered to be touched when the user has
25158 * first focused the control element and then shifted focus away from the control (blur event).
25160 this.$setTouched = function() {
25161 ctrl.$touched = true;
25162 ctrl.$untouched = false;
25163 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25168 * @name ngModel.NgModelController#$rollbackViewValue
25171 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25172 * which may be caused by a pending debounced event or because the input is waiting for a some
25175 * If you have an input that uses `ng-model-options` to set up debounced events or events such
25176 * as blur you can have a situation where there is a period when the `$viewValue`
25177 * is out of synch with the ngModel's `$modelValue`.
25179 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25180 * programmatically before these debounced/future events have resolved/occurred, because Angular's
25181 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25183 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25184 * input which may have such events pending. This is important in order to make sure that the
25185 * input field will be updated with the new model value and any pending operations are cancelled.
25187 * <example name="ng-model-cancel-update" module="cancel-update-example">
25188 * <file name="app.js">
25189 * angular.module('cancel-update-example', [])
25191 * .controller('CancelUpdateController', ['$scope', function($scope) {
25192 * $scope.resetWithCancel = function(e) {
25193 * if (e.keyCode == 27) {
25194 * $scope.myForm.myInput1.$rollbackViewValue();
25195 * $scope.myValue = '';
25198 * $scope.resetWithoutCancel = function(e) {
25199 * if (e.keyCode == 27) {
25200 * $scope.myValue = '';
25205 * <file name="index.html">
25206 * <div ng-controller="CancelUpdateController">
25207 * <p>Try typing something in each input. See that the model only updates when you
25208 * blur off the input.
25210 * <p>Now see what happens if you start typing then press the Escape key</p>
25212 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25213 * <p id="inputDescription1">With $rollbackViewValue()</p>
25214 * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25215 * ng-keydown="resetWithCancel($event)"><br/>
25216 * myValue: "{{ myValue }}"
25218 * <p id="inputDescription2">Without $rollbackViewValue()</p>
25219 * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25220 * ng-keydown="resetWithoutCancel($event)"><br/>
25221 * myValue: "{{ myValue }}"
25227 this.$rollbackViewValue = function() {
25228 $timeout.cancel(pendingDebounce);
25229 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25235 * @name ngModel.NgModelController#$validate
25238 * Runs each of the registered validators (first synchronous validators and then
25239 * asynchronous validators).
25240 * If the validity changes to invalid, the model will be set to `undefined`,
25241 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25242 * If the validity changes to valid, it will set the model to the last available valid
25243 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25245 this.$validate = function() {
25246 // ignore $validate before model is initialized
25247 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25251 var viewValue = ctrl.$$lastCommittedViewValue;
25252 // Note: we use the $$rawModelValue as $modelValue might have been
25253 // set to undefined during a view -> model update that found validation
25254 // errors. We can't parse the view here, since that could change
25255 // the model although neither viewValue nor the model on the scope changed
25256 var modelValue = ctrl.$$rawModelValue;
25258 var prevValid = ctrl.$valid;
25259 var prevModelValue = ctrl.$modelValue;
25261 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25263 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25264 // If there was no change in validity, don't update the model
25265 // This prevents changing an invalid modelValue to undefined
25266 if (!allowInvalid && prevValid !== allValid) {
25267 // Note: Don't check ctrl.$valid here, as we could have
25268 // external validators (e.g. calculated on the server),
25269 // that just call $setValidity and need the model value
25270 // to calculate their validity.
25271 ctrl.$modelValue = allValid ? modelValue : undefined;
25273 if (ctrl.$modelValue !== prevModelValue) {
25274 ctrl.$$writeModelToScope();
25281 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25282 currentValidationRunId++;
25283 var localValidationRunId = currentValidationRunId;
25285 // check parser error
25286 if (!processParseErrors()) {
25287 validationDone(false);
25290 if (!processSyncValidators()) {
25291 validationDone(false);
25294 processAsyncValidators();
25296 function processParseErrors() {
25297 var errorKey = ctrl.$$parserName || 'parse';
25298 if (isUndefined(parserValid)) {
25299 setValidity(errorKey, null);
25301 if (!parserValid) {
25302 forEach(ctrl.$validators, function(v, name) {
25303 setValidity(name, null);
25305 forEach(ctrl.$asyncValidators, function(v, name) {
25306 setValidity(name, null);
25309 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25310 setValidity(errorKey, parserValid);
25311 return parserValid;
25316 function processSyncValidators() {
25317 var syncValidatorsValid = true;
25318 forEach(ctrl.$validators, function(validator, name) {
25319 var result = validator(modelValue, viewValue);
25320 syncValidatorsValid = syncValidatorsValid && result;
25321 setValidity(name, result);
25323 if (!syncValidatorsValid) {
25324 forEach(ctrl.$asyncValidators, function(v, name) {
25325 setValidity(name, null);
25332 function processAsyncValidators() {
25333 var validatorPromises = [];
25334 var allValid = true;
25335 forEach(ctrl.$asyncValidators, function(validator, name) {
25336 var promise = validator(modelValue, viewValue);
25337 if (!isPromiseLike(promise)) {
25338 throw ngModelMinErr("$asyncValidators",
25339 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25341 setValidity(name, undefined);
25342 validatorPromises.push(promise.then(function() {
25343 setValidity(name, true);
25344 }, function(error) {
25346 setValidity(name, false);
25349 if (!validatorPromises.length) {
25350 validationDone(true);
25352 $q.all(validatorPromises).then(function() {
25353 validationDone(allValid);
25358 function setValidity(name, isValid) {
25359 if (localValidationRunId === currentValidationRunId) {
25360 ctrl.$setValidity(name, isValid);
25364 function validationDone(allValid) {
25365 if (localValidationRunId === currentValidationRunId) {
25367 doneCallback(allValid);
25374 * @name ngModel.NgModelController#$commitViewValue
25377 * Commit a pending update to the `$modelValue`.
25379 * Updates may be pending by a debounced event or because the input is waiting for a some future
25380 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25381 * usually handles calling this in response to input events.
25383 this.$commitViewValue = function() {
25384 var viewValue = ctrl.$viewValue;
25386 $timeout.cancel(pendingDebounce);
25388 // If the view value has not changed then we should just exit, except in the case where there is
25389 // a native validator on the element. In this case the validation state may have changed even though
25390 // the viewValue has stayed empty.
25391 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25394 ctrl.$$lastCommittedViewValue = viewValue;
25397 if (ctrl.$pristine) {
25400 this.$$parseAndValidate();
25403 this.$$parseAndValidate = function() {
25404 var viewValue = ctrl.$$lastCommittedViewValue;
25405 var modelValue = viewValue;
25406 parserValid = isUndefined(modelValue) ? undefined : true;
25409 for (var i = 0; i < ctrl.$parsers.length; i++) {
25410 modelValue = ctrl.$parsers[i](modelValue);
25411 if (isUndefined(modelValue)) {
25412 parserValid = false;
25417 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25418 // ctrl.$modelValue has not been touched yet...
25419 ctrl.$modelValue = ngModelGet($scope);
25421 var prevModelValue = ctrl.$modelValue;
25422 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25423 ctrl.$$rawModelValue = modelValue;
25425 if (allowInvalid) {
25426 ctrl.$modelValue = modelValue;
25427 writeToModelIfNeeded();
25430 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25431 // This can happen if e.g. $setViewValue is called from inside a parser
25432 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25433 if (!allowInvalid) {
25434 // Note: Don't check ctrl.$valid here, as we could have
25435 // external validators (e.g. calculated on the server),
25436 // that just call $setValidity and need the model value
25437 // to calculate their validity.
25438 ctrl.$modelValue = allValid ? modelValue : undefined;
25439 writeToModelIfNeeded();
25443 function writeToModelIfNeeded() {
25444 if (ctrl.$modelValue !== prevModelValue) {
25445 ctrl.$$writeModelToScope();
25450 this.$$writeModelToScope = function() {
25451 ngModelSet($scope, ctrl.$modelValue);
25452 forEach(ctrl.$viewChangeListeners, function(listener) {
25456 $exceptionHandler(e);
25463 * @name ngModel.NgModelController#$setViewValue
25466 * Update the view value.
25468 * This method should be called when a control wants to change the view value; typically,
25469 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25470 * directive calls it when the value of the input changes and {@link ng.directive:select select}
25471 * calls it when an option is selected.
25473 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25474 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25475 * value sent directly for processing, finally to be applied to `$modelValue` and then the
25476 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25477 * in the `$viewChangeListeners` list, are called.
25479 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25480 * and the `default` trigger is not listed, all those actions will remain pending until one of the
25481 * `updateOn` events is triggered on the DOM element.
25482 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25483 * directive is used with a custom debounce for this particular event.
25484 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25485 * is specified, once the timer runs out.
25487 * When used with standard inputs, the view value will always be a string (which is in some cases
25488 * parsed into another type, such as a `Date` object for `input[date]`.)
25489 * However, custom controls might also pass objects to this method. In this case, we should make
25490 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25491 * perform a deep watch of objects, it only looks for a change of identity. If you only change
25492 * the property of the object then ngModel will not realise that the object has changed and
25493 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25494 * not change properties of the copy once it has been passed to `$setViewValue`.
25495 * Otherwise you may cause the model value on the scope to change incorrectly.
25497 * <div class="alert alert-info">
25498 * In any case, the value passed to the method should always reflect the current value
25499 * of the control. For example, if you are calling `$setViewValue` for an input element,
25500 * you should pass the input DOM value. Otherwise, the control and the scope model become
25501 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25502 * the control's DOM value in any way. If we want to change the control's DOM value
25503 * programmatically, we should update the `ngModel` scope expression. Its new value will be
25504 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25505 * to update the DOM, and finally call `$validate` on it.
25508 * @param {*} value value from the view.
25509 * @param {string} trigger Event that triggered the update.
25511 this.$setViewValue = function(value, trigger) {
25512 ctrl.$viewValue = value;
25513 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25514 ctrl.$$debounceViewValueCommit(trigger);
25518 this.$$debounceViewValueCommit = function(trigger) {
25519 var debounceDelay = 0,
25520 options = ctrl.$options,
25523 if (options && isDefined(options.debounce)) {
25524 debounce = options.debounce;
25525 if (isNumber(debounce)) {
25526 debounceDelay = debounce;
25527 } else if (isNumber(debounce[trigger])) {
25528 debounceDelay = debounce[trigger];
25529 } else if (isNumber(debounce['default'])) {
25530 debounceDelay = debounce['default'];
25534 $timeout.cancel(pendingDebounce);
25535 if (debounceDelay) {
25536 pendingDebounce = $timeout(function() {
25537 ctrl.$commitViewValue();
25539 } else if ($rootScope.$$phase) {
25540 ctrl.$commitViewValue();
25542 $scope.$apply(function() {
25543 ctrl.$commitViewValue();
25549 // Note: we cannot use a normal scope.$watch as we want to detect the following:
25550 // 1. scope value is 'a'
25551 // 2. user enters 'b'
25552 // 3. ng-change kicks in and reverts scope value to 'a'
25553 // -> scope value did not change since the last digest as
25554 // ng-change executes in apply phase
25555 // 4. view should be changed back to 'a'
25556 $scope.$watch(function ngModelWatch() {
25557 var modelValue = ngModelGet($scope);
25559 // if scope model value and ngModel value are out of sync
25560 // TODO(perf): why not move this to the action fn?
25561 if (modelValue !== ctrl.$modelValue &&
25562 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25563 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25565 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25566 parserValid = undefined;
25568 var formatters = ctrl.$formatters,
25569 idx = formatters.length;
25571 var viewValue = modelValue;
25573 viewValue = formatters[idx](viewValue);
25575 if (ctrl.$viewValue !== viewValue) {
25576 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25579 ctrl.$$runValidators(modelValue, viewValue, noop);
25596 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25597 * property on the scope using {@link ngModel.NgModelController NgModelController},
25598 * which is created and exposed by this directive.
25600 * `ngModel` is responsible for:
25602 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25604 * - Providing validation behavior (i.e. required, number, email, url).
25605 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25606 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25607 * - Registering the control with its parent {@link ng.directive:form form}.
25609 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25610 * current scope. If the property doesn't already exist on this scope, it will be created
25611 * implicitly and added to the scope.
25613 * For best practices on using `ngModel`, see:
25615 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25617 * For basic examples, how to use `ngModel`, see:
25619 * - {@link ng.directive:input input}
25620 * - {@link input[text] text}
25621 * - {@link input[checkbox] checkbox}
25622 * - {@link input[radio] radio}
25623 * - {@link input[number] number}
25624 * - {@link input[email] email}
25625 * - {@link input[url] url}
25626 * - {@link input[date] date}
25627 * - {@link input[datetime-local] datetime-local}
25628 * - {@link input[time] time}
25629 * - {@link input[month] month}
25630 * - {@link input[week] week}
25631 * - {@link ng.directive:select select}
25632 * - {@link ng.directive:textarea textarea}
25635 * The following CSS classes are added and removed on the associated input/select/textarea element
25636 * depending on the validity of the model.
25638 * - `ng-valid`: the model is valid
25639 * - `ng-invalid`: the model is invalid
25640 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
25641 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25642 * - `ng-pristine`: the control hasn't been interacted with yet
25643 * - `ng-dirty`: the control has been interacted with
25644 * - `ng-touched`: the control has been blurred
25645 * - `ng-untouched`: the control hasn't been blurred
25646 * - `ng-pending`: any `$asyncValidators` are unfulfilled
25648 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25650 * ## Animation Hooks
25652 * Animations within models are triggered when any of the associated CSS classes are added and removed
25653 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25654 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25655 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25656 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25658 * The following example shows a simple way to utilize CSS transitions to style an input element
25659 * that has been rendered as invalid after it has been validated:
25662 * //be sure to include ngAnimate as a module to hook into more
25663 * //advanced animations
25665 * transition:0.5s linear all;
25666 * background: white;
25668 * .my-input.ng-invalid {
25675 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25676 <file name="index.html">
25678 angular.module('inputExample', [])
25679 .controller('ExampleController', ['$scope', function($scope) {
25685 transition:all linear 0.5s;
25686 background: transparent;
25688 .my-input.ng-invalid {
25693 <p id="inputDescription">
25694 Update input to see transitions when valid/invalid.
25695 Integer is a valid value.
25697 <form name="testForm" ng-controller="ExampleController">
25698 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25699 aria-describedby="inputDescription" />
25704 * ## Binding to a getter/setter
25706 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
25707 * function that returns a representation of the model when called with zero arguments, and sets
25708 * the internal state of a model when called with an argument. It's sometimes useful to use this
25709 * for models that have an internal representation that's different from what the model exposes
25712 * <div class="alert alert-success">
25713 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25714 * frequently than other parts of your code.
25717 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25718 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25719 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25720 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25722 * The following example shows how to use `ngModel` with a getter/setter:
25725 * <example name="ngModel-getter-setter" module="getterSetterExample">
25726 <file name="index.html">
25727 <div ng-controller="ExampleController">
25728 <form name="userForm">
25730 <input type="text" name="userName"
25731 ng-model="user.name"
25732 ng-model-options="{ getterSetter: true }" />
25735 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25738 <file name="app.js">
25739 angular.module('getterSetterExample', [])
25740 .controller('ExampleController', ['$scope', function($scope) {
25741 var _name = 'Brian';
25743 name: function(newName) {
25744 // Note that newName can be undefined for two reasons:
25745 // 1. Because it is called as a getter and thus called with no arguments
25746 // 2. Because the property should actually be set to undefined. This happens e.g. if the
25747 // input is invalid
25748 return arguments.length ? (_name = newName) : _name;
25755 var ngModelDirective = ['$rootScope', function($rootScope) {
25758 require: ['ngModel', '^?form', '^?ngModelOptions'],
25759 controller: NgModelController,
25760 // Prelink needs to run before any input directive
25761 // so that we can set the NgModelOptions in NgModelController
25762 // before anyone else uses it.
25764 compile: function ngModelCompile(element) {
25765 // Setup initial state of the control
25766 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25769 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25770 var modelCtrl = ctrls[0],
25771 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25773 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25775 // notify others, especially parent forms
25776 formCtrl.$addControl(modelCtrl);
25778 attr.$observe('name', function(newValue) {
25779 if (modelCtrl.$name !== newValue) {
25780 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25784 scope.$on('$destroy', function() {
25785 modelCtrl.$$parentForm.$removeControl(modelCtrl);
25788 post: function ngModelPostLink(scope, element, attr, ctrls) {
25789 var modelCtrl = ctrls[0];
25790 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25791 element.on(modelCtrl.$options.updateOn, function(ev) {
25792 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25796 element.on('blur', function(ev) {
25797 if (modelCtrl.$touched) return;
25799 if ($rootScope.$$phase) {
25800 scope.$evalAsync(modelCtrl.$setTouched);
25802 scope.$apply(modelCtrl.$setTouched);
25811 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25815 * @name ngModelOptions
25818 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25819 * events that will trigger a model update and/or a debouncing delay so that the actual update only
25820 * takes place when a timer expires; this timer will be reset after another change takes place.
25822 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25823 * be different from the value in the actual model. This means that if you update the model you
25824 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25825 * order to make sure it is synchronized with the model and that any debounced action is canceled.
25827 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25828 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25829 * important because `form` controllers are published to the related scope under the name in their
25830 * `name` attribute.
25832 * Any pending changes will take place immediately when an enclosing form is submitted via the
25833 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25834 * to have access to the updated model.
25836 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25838 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25839 * - `updateOn`: string specifying which event should the input be bound to. You can set several
25840 * events using an space delimited list. There is a special event called `default` that
25841 * matches the default events belonging of the control.
25842 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25843 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25844 * custom value for each event. For example:
25845 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25846 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25847 * not validate correctly instead of the default behavior of setting the model to undefined.
25848 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25849 `ngModel` as getters/setters.
25850 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25851 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25852 * continental US time zone abbreviations, but for general use, use a time zone offset, for
25853 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25854 * If not specified, the timezone of the browser will be used.
25858 The following example shows how to override immediate updates. Changes on the inputs within the
25859 form will update the model only when the control loses focus (blur event). If `escape` key is
25860 pressed while the input field is focused, the value is reset to the value in the current model.
25862 <example name="ngModelOptions-directive-blur" module="optionsExample">
25863 <file name="index.html">
25864 <div ng-controller="ExampleController">
25865 <form name="userForm">
25867 <input type="text" name="userName"
25868 ng-model="user.name"
25869 ng-model-options="{ updateOn: 'blur' }"
25870 ng-keyup="cancel($event)" />
25873 <input type="text" ng-model="user.data" />
25876 <pre>user.name = <span ng-bind="user.name"></span></pre>
25877 <pre>user.data = <span ng-bind="user.data"></span></pre>
25880 <file name="app.js">
25881 angular.module('optionsExample', [])
25882 .controller('ExampleController', ['$scope', function($scope) {
25883 $scope.user = { name: 'John', data: '' };
25885 $scope.cancel = function(e) {
25886 if (e.keyCode == 27) {
25887 $scope.userForm.userName.$rollbackViewValue();
25892 <file name="protractor.js" type="protractor">
25893 var model = element(by.binding('user.name'));
25894 var input = element(by.model('user.name'));
25895 var other = element(by.model('user.data'));
25897 it('should allow custom events', function() {
25898 input.sendKeys(' Doe');
25900 expect(model.getText()).toEqual('John');
25902 expect(model.getText()).toEqual('John Doe');
25905 it('should $rollbackViewValue when model changes', function() {
25906 input.sendKeys(' Doe');
25907 expect(input.getAttribute('value')).toEqual('John Doe');
25908 input.sendKeys(protractor.Key.ESCAPE);
25909 expect(input.getAttribute('value')).toEqual('John');
25911 expect(model.getText()).toEqual('John');
25916 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25917 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25919 <example name="ngModelOptions-directive-debounce" module="optionsExample">
25920 <file name="index.html">
25921 <div ng-controller="ExampleController">
25922 <form name="userForm">
25924 <input type="text" name="userName"
25925 ng-model="user.name"
25926 ng-model-options="{ debounce: 1000 }" />
25928 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25931 <pre>user.name = <span ng-bind="user.name"></span></pre>
25934 <file name="app.js">
25935 angular.module('optionsExample', [])
25936 .controller('ExampleController', ['$scope', function($scope) {
25937 $scope.user = { name: 'Igor' };
25942 This one shows how to bind to getter/setters:
25944 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25945 <file name="index.html">
25946 <div ng-controller="ExampleController">
25947 <form name="userForm">
25949 <input type="text" name="userName"
25950 ng-model="user.name"
25951 ng-model-options="{ getterSetter: true }" />
25954 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25957 <file name="app.js">
25958 angular.module('getterSetterExample', [])
25959 .controller('ExampleController', ['$scope', function($scope) {
25960 var _name = 'Brian';
25962 name: function(newName) {
25963 // Note that newName can be undefined for two reasons:
25964 // 1. Because it is called as a getter and thus called with no arguments
25965 // 2. Because the property should actually be set to undefined. This happens e.g. if the
25966 // input is invalid
25967 return arguments.length ? (_name = newName) : _name;
25974 var ngModelOptionsDirective = function() {
25977 controller: ['$scope', '$attrs', function($scope, $attrs) {
25979 this.$options = copy($scope.$eval($attrs.ngModelOptions));
25980 // Allow adding/overriding bound events
25981 if (isDefined(this.$options.updateOn)) {
25982 this.$options.updateOnDefault = false;
25983 // extract "default" pseudo-event from list of events that can trigger a model update
25984 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25985 that.$options.updateOnDefault = true;
25989 this.$options.updateOnDefault = true;
25998 function addSetValidityMethod(context) {
25999 var ctrl = context.ctrl,
26000 $element = context.$element,
26003 unset = context.unset,
26004 $animate = context.$animate;
26006 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26008 ctrl.$setValidity = setValidity;
26010 function setValidity(validationErrorKey, state, controller) {
26011 if (isUndefined(state)) {
26012 createAndSet('$pending', validationErrorKey, controller);
26014 unsetAndCleanup('$pending', validationErrorKey, controller);
26016 if (!isBoolean(state)) {
26017 unset(ctrl.$error, validationErrorKey, controller);
26018 unset(ctrl.$$success, validationErrorKey, controller);
26021 unset(ctrl.$error, validationErrorKey, controller);
26022 set(ctrl.$$success, validationErrorKey, controller);
26024 set(ctrl.$error, validationErrorKey, controller);
26025 unset(ctrl.$$success, validationErrorKey, controller);
26028 if (ctrl.$pending) {
26029 cachedToggleClass(PENDING_CLASS, true);
26030 ctrl.$valid = ctrl.$invalid = undefined;
26031 toggleValidationCss('', null);
26033 cachedToggleClass(PENDING_CLASS, false);
26034 ctrl.$valid = isObjectEmpty(ctrl.$error);
26035 ctrl.$invalid = !ctrl.$valid;
26036 toggleValidationCss('', ctrl.$valid);
26039 // re-read the state as the set/unset methods could have
26040 // combined state in ctrl.$error[validationError] (used for forms),
26041 // where setting/unsetting only increments/decrements the value,
26042 // and does not replace it.
26044 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26045 combinedState = undefined;
26046 } else if (ctrl.$error[validationErrorKey]) {
26047 combinedState = false;
26048 } else if (ctrl.$$success[validationErrorKey]) {
26049 combinedState = true;
26051 combinedState = null;
26054 toggleValidationCss(validationErrorKey, combinedState);
26055 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26058 function createAndSet(name, value, controller) {
26062 set(ctrl[name], value, controller);
26065 function unsetAndCleanup(name, value, controller) {
26067 unset(ctrl[name], value, controller);
26069 if (isObjectEmpty(ctrl[name])) {
26070 ctrl[name] = undefined;
26074 function cachedToggleClass(className, switchValue) {
26075 if (switchValue && !classCache[className]) {
26076 $animate.addClass($element, className);
26077 classCache[className] = true;
26078 } else if (!switchValue && classCache[className]) {
26079 $animate.removeClass($element, className);
26080 classCache[className] = false;
26084 function toggleValidationCss(validationErrorKey, isValid) {
26085 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26087 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26088 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26092 function isObjectEmpty(obj) {
26094 for (var prop in obj) {
26095 if (obj.hasOwnProperty(prop)) {
26105 * @name ngNonBindable
26110 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26111 * DOM element. This is useful if the element contains what appears to be Angular directives and
26112 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26113 * displays snippets of code, for instance.
26118 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26119 * but the one wrapped in `ngNonBindable` is left alone.
26123 <file name="index.html">
26124 <div>Normal: {{1 + 2}}</div>
26125 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26127 <file name="protractor.js" type="protractor">
26128 it('should check ng-non-bindable', function() {
26129 expect(element(by.binding('1 + 2')).getText()).toContain('3');
26130 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26135 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26137 /* global jqLiteRemove */
26139 var ngOptionsMinErr = minErr('ngOptions');
26148 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26149 * elements for the `<select>` element using the array or object obtained by evaluating the
26150 * `ngOptions` comprehension expression.
26152 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26153 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26154 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26155 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26156 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26157 * to a non-string value. This is because an option element can only be bound to string values at
26160 * When an item in the `<select>` menu is selected, the array element or object property
26161 * represented by the selected option will be bound to the model identified by the `ngModel`
26164 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26165 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26166 * option. See example below for demonstration.
26168 * ## Complex Models (objects or collections)
26170 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26171 * binding the select to a model that is an object or a collection.
26173 * One issue occurs if you want to preselect an option. For example, if you set
26174 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26175 * because the objects are not identical. So by default, you should always reference the item in your collection
26176 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26178 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26179 * of the item not by reference, but by the result of the `track by` expression. For example, if your
26180 * collection items have an id property, you would `track by item.id`.
26182 * A different issue with objects or collections is that ngModel won't detect if an object property or
26183 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26184 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26185 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26186 * has not changed identity, but only a property on the object or an item in the collection changes.
26188 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26189 * if the model is an array). This means that changing a property deeper than the first level inside the
26190 * object/collection will not trigger a re-rendering.
26192 * ## `select` **`as`**
26194 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26195 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26196 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26197 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26200 * ### `select` **`as`** and **`track by`**
26202 * <div class="alert alert-warning">
26203 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26206 * Given this array of items on the $scope:
26209 * $scope.items = [{
26212 * subItem: { name: 'aSubItem' }
26216 * subItem: { name: 'bSubItem' }
26223 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26226 * $scope.selected = $scope.items[0];
26229 * but this will not work:
26232 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26235 * $scope.selected = $scope.items[0].subItem;
26238 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26239 * `items` array. Because the selected option has been set programmatically in the controller, the
26240 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26241 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26242 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26243 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26244 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26247 * @param {string} ngModel Assignable angular expression to data-bind to.
26248 * @param {string=} name Property name of the form under which the control is published.
26249 * @param {string=} required The control is considered valid only if value is entered.
26250 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26251 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26252 * `required` when you want to data-bind to the `required` attribute.
26253 * @param {comprehension_expression=} ngOptions in one of the following forms:
26255 * * for array data sources:
26256 * * `label` **`for`** `value` **`in`** `array`
26257 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26258 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26259 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26260 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26261 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26262 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26263 * (for including a filter with `track by`)
26264 * * for object data sources:
26265 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26266 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26267 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26268 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26269 * * `select` **`as`** `label` **`group by`** `group`
26270 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26271 * * `select` **`as`** `label` **`disable when`** `disable`
26272 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26276 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26277 * * `value`: local variable which will refer to each item in the `array` or each property value
26278 * of `object` during iteration.
26279 * * `key`: local variable which will refer to a property name in `object` during iteration.
26280 * * `label`: The result of this expression will be the label for `<option>` element. The
26281 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26282 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
26283 * element. If not specified, `select` expression will default to `value`.
26284 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
26286 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
26287 * element. Return `true` to disable.
26288 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26289 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
26290 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26291 * even when the options are recreated (e.g. reloaded from the server).
26294 <example module="selectExample">
26295 <file name="index.html">
26297 angular.module('selectExample', [])
26298 .controller('ExampleController', ['$scope', function($scope) {
26300 {name:'black', shade:'dark'},
26301 {name:'white', shade:'light', notAnOption: true},
26302 {name:'red', shade:'dark'},
26303 {name:'blue', shade:'dark', notAnOption: true},
26304 {name:'yellow', shade:'light', notAnOption: false}
26306 $scope.myColor = $scope.colors[2]; // red
26309 <div ng-controller="ExampleController">
26311 <li ng-repeat="color in colors">
26312 <label>Name: <input ng-model="color.name"></label>
26313 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26314 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26317 <button ng-click="colors.push({})">add</button>
26321 <label>Color (null not allowed):
26322 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26324 <label>Color (null allowed):
26325 <span class="nullable">
26326 <select ng-model="myColor" ng-options="color.name for color in colors">
26327 <option value="">-- choose color --</option>
26329 </span></label><br/>
26331 <label>Color grouped by shade:
26332 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26336 <label>Color grouped by shade, with some disabled:
26337 <select ng-model="myColor"
26338 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26344 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26347 Currently selected: {{ {selected_color:myColor} }}
26348 <div style="border:solid 1px black; height:20px"
26349 ng-style="{'background-color':myColor.name}">
26353 <file name="protractor.js" type="protractor">
26354 it('should check ng-options', function() {
26355 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26356 element.all(by.model('myColor')).first().click();
26357 element.all(by.css('select[ng-model="myColor"] option')).first().click();
26358 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26359 element(by.css('.nullable select[ng-model="myColor"]')).click();
26360 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26361 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26367 // jshint maxlen: false
26368 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26369 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
26370 // 1: value expression (valueFn)
26371 // 2: label expression (displayFn)
26372 // 3: group by expression (groupByFn)
26373 // 4: disable when expression (disableWhenFn)
26374 // 5: array item variable name
26375 // 6: object item key variable name
26376 // 7: object item value variable name
26377 // 8: collection expression
26378 // 9: track by expression
26379 // jshint maxlen: 100
26382 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26384 function parseOptionsExpression(optionsExp, selectElement, scope) {
26386 var match = optionsExp.match(NG_OPTIONS_REGEXP);
26388 throw ngOptionsMinErr('iexp',
26389 "Expected expression in form of " +
26390 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26391 " but got '{0}'. Element: {1}",
26392 optionsExp, startingTag(selectElement));
26395 // Extract the parts from the ngOptions expression
26397 // The variable name for the value of the item in the collection
26398 var valueName = match[5] || match[7];
26399 // The variable name for the key of the item in the collection
26400 var keyName = match[6];
26402 // An expression that generates the viewValue for an option if there is a label expression
26403 var selectAs = / as /.test(match[0]) && match[1];
26404 // An expression that is used to track the id of each object in the options collection
26405 var trackBy = match[9];
26406 // An expression that generates the viewValue for an option if there is no label expression
26407 var valueFn = $parse(match[2] ? match[1] : valueName);
26408 var selectAsFn = selectAs && $parse(selectAs);
26409 var viewValueFn = selectAsFn || valueFn;
26410 var trackByFn = trackBy && $parse(trackBy);
26412 // Get the value by which we are going to track the option
26413 // if we have a trackFn then use that (passing scope and locals)
26414 // otherwise just hash the given viewValue
26415 var getTrackByValueFn = trackBy ?
26416 function(value, locals) { return trackByFn(scope, locals); } :
26417 function getHashOfValue(value) { return hashKey(value); };
26418 var getTrackByValue = function(value, key) {
26419 return getTrackByValueFn(value, getLocals(value, key));
26422 var displayFn = $parse(match[2] || match[1]);
26423 var groupByFn = $parse(match[3] || '');
26424 var disableWhenFn = $parse(match[4] || '');
26425 var valuesFn = $parse(match[8]);
26428 var getLocals = keyName ? function(value, key) {
26429 locals[keyName] = key;
26430 locals[valueName] = value;
26432 } : function(value) {
26433 locals[valueName] = value;
26438 function Option(selectValue, viewValue, label, group, disabled) {
26439 this.selectValue = selectValue;
26440 this.viewValue = viewValue;
26441 this.label = label;
26442 this.group = group;
26443 this.disabled = disabled;
26446 function getOptionValuesKeys(optionValues) {
26447 var optionValuesKeys;
26449 if (!keyName && isArrayLike(optionValues)) {
26450 optionValuesKeys = optionValues;
26452 // if object, extract keys, in enumeration order, unsorted
26453 optionValuesKeys = [];
26454 for (var itemKey in optionValues) {
26455 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26456 optionValuesKeys.push(itemKey);
26460 return optionValuesKeys;
26465 getTrackByValue: getTrackByValue,
26466 getWatchables: $parse(valuesFn, function(optionValues) {
26467 // Create a collection of things that we would like to watch (watchedArray)
26468 // so that they can all be watched using a single $watchCollection
26469 // that only runs the handler once if anything changes
26470 var watchedArray = [];
26471 optionValues = optionValues || [];
26473 var optionValuesKeys = getOptionValuesKeys(optionValues);
26474 var optionValuesLength = optionValuesKeys.length;
26475 for (var index = 0; index < optionValuesLength; index++) {
26476 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26477 var value = optionValues[key];
26479 var locals = getLocals(optionValues[key], key);
26480 var selectValue = getTrackByValueFn(optionValues[key], locals);
26481 watchedArray.push(selectValue);
26483 // Only need to watch the displayFn if there is a specific label expression
26484 if (match[2] || match[1]) {
26485 var label = displayFn(scope, locals);
26486 watchedArray.push(label);
26489 // Only need to watch the disableWhenFn if there is a specific disable expression
26491 var disableWhen = disableWhenFn(scope, locals);
26492 watchedArray.push(disableWhen);
26495 return watchedArray;
26498 getOptions: function() {
26500 var optionItems = [];
26501 var selectValueMap = {};
26503 // The option values were already computed in the `getWatchables` fn,
26504 // which must have been called to trigger `getOptions`
26505 var optionValues = valuesFn(scope) || [];
26506 var optionValuesKeys = getOptionValuesKeys(optionValues);
26507 var optionValuesLength = optionValuesKeys.length;
26509 for (var index = 0; index < optionValuesLength; index++) {
26510 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26511 var value = optionValues[key];
26512 var locals = getLocals(value, key);
26513 var viewValue = viewValueFn(scope, locals);
26514 var selectValue = getTrackByValueFn(viewValue, locals);
26515 var label = displayFn(scope, locals);
26516 var group = groupByFn(scope, locals);
26517 var disabled = disableWhenFn(scope, locals);
26518 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26520 optionItems.push(optionItem);
26521 selectValueMap[selectValue] = optionItem;
26525 items: optionItems,
26526 selectValueMap: selectValueMap,
26527 getOptionFromViewValue: function(value) {
26528 return selectValueMap[getTrackByValue(value)];
26530 getViewValueFromOption: function(option) {
26531 // If the viewValue could be an object that may be mutated by the application,
26532 // we need to make a copy and not return the reference to the value on the option.
26533 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26541 // we can't just jqLite('<option>') since jqLite is not smart enough
26542 // to create it in <select> and IE barfs otherwise.
26543 var optionTemplate = document.createElement('option'),
26544 optGroupTemplate = document.createElement('optgroup');
26547 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26549 // if ngModel is not defined, we don't need to do anything
26550 var ngModelCtrl = ctrls[1];
26551 if (!ngModelCtrl) return;
26553 var selectCtrl = ctrls[0];
26554 var multiple = attr.multiple;
26556 // The emptyOption allows the application developer to provide their own custom "empty"
26557 // option when the viewValue does not match any of the option values.
26559 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26560 if (children[i].value === '') {
26561 emptyOption = children.eq(i);
26566 var providedEmptyOption = !!emptyOption;
26568 var unknownOption = jqLite(optionTemplate.cloneNode(false));
26569 unknownOption.val('?');
26572 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26575 var renderEmptyOption = function() {
26576 if (!providedEmptyOption) {
26577 selectElement.prepend(emptyOption);
26579 selectElement.val('');
26580 emptyOption.prop('selected', true); // needed for IE
26581 emptyOption.attr('selected', true);
26584 var removeEmptyOption = function() {
26585 if (!providedEmptyOption) {
26586 emptyOption.remove();
26591 var renderUnknownOption = function() {
26592 selectElement.prepend(unknownOption);
26593 selectElement.val('?');
26594 unknownOption.prop('selected', true); // needed for IE
26595 unknownOption.attr('selected', true);
26598 var removeUnknownOption = function() {
26599 unknownOption.remove();
26602 // Update the controller methods for multiple selectable options
26605 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26606 var option = options.getOptionFromViewValue(value);
26608 if (option && !option.disabled) {
26609 if (selectElement[0].value !== option.selectValue) {
26610 removeUnknownOption();
26611 removeEmptyOption();
26613 selectElement[0].value = option.selectValue;
26614 option.element.selected = true;
26615 option.element.setAttribute('selected', 'selected');
26618 if (value === null || providedEmptyOption) {
26619 removeUnknownOption();
26620 renderEmptyOption();
26622 removeEmptyOption();
26623 renderUnknownOption();
26628 selectCtrl.readValue = function readNgOptionsValue() {
26630 var selectedOption = options.selectValueMap[selectElement.val()];
26632 if (selectedOption && !selectedOption.disabled) {
26633 removeEmptyOption();
26634 removeUnknownOption();
26635 return options.getViewValueFromOption(selectedOption);
26640 // If we are using `track by` then we must watch the tracked value on the model
26641 // since ngModel only watches for object identity change
26642 if (ngOptions.trackBy) {
26644 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26645 function() { ngModelCtrl.$render(); }
26651 ngModelCtrl.$isEmpty = function(value) {
26652 return !value || value.length === 0;
26656 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26657 options.items.forEach(function(option) {
26658 option.element.selected = false;
26662 value.forEach(function(item) {
26663 var option = options.getOptionFromViewValue(item);
26664 if (option && !option.disabled) option.element.selected = true;
26670 selectCtrl.readValue = function readNgOptionsMultiple() {
26671 var selectedValues = selectElement.val() || [],
26674 forEach(selectedValues, function(value) {
26675 var option = options.selectValueMap[value];
26676 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26682 // If we are using `track by` then we must watch these tracked values on the model
26683 // since ngModel only watches for object identity change
26684 if (ngOptions.trackBy) {
26686 scope.$watchCollection(function() {
26687 if (isArray(ngModelCtrl.$viewValue)) {
26688 return ngModelCtrl.$viewValue.map(function(value) {
26689 return ngOptions.getTrackByValue(value);
26693 ngModelCtrl.$render();
26700 if (providedEmptyOption) {
26702 // we need to remove it before calling selectElement.empty() because otherwise IE will
26703 // remove the label from the element. wtf?
26704 emptyOption.remove();
26706 // compile the element since there might be bindings in it
26707 $compile(emptyOption)(scope);
26709 // remove the class, which is added automatically because we recompile the element and it
26710 // becomes the compilation root
26711 emptyOption.removeClass('ng-scope');
26713 emptyOption = jqLite(optionTemplate.cloneNode(false));
26716 // We need to do this here to ensure that the options object is defined
26717 // when we first hit it in writeNgOptionsValue
26720 // We will re-render the option elements if the option values or labels change
26721 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26723 // ------------------------------------------------------------------ //
26726 function updateOptionElement(option, element) {
26727 option.element = element;
26728 element.disabled = option.disabled;
26729 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26730 // selects in certain circumstances when multiple selects are next to each other and display
26731 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26732 // See https://github.com/angular/angular.js/issues/11314 for more info.
26733 // This is unfortunately untestable with unit / e2e tests
26734 if (option.label !== element.label) {
26735 element.label = option.label;
26736 element.textContent = option.label;
26738 if (option.value !== element.value) element.value = option.selectValue;
26741 function addOrReuseElement(parent, current, type, templateElement) {
26743 // Check whether we can reuse the next element
26744 if (current && lowercase(current.nodeName) === type) {
26745 // The next element is the right type so reuse it
26748 // The next element is not the right type so create a new one
26749 element = templateElement.cloneNode(false);
26751 // There are no more elements so just append it to the select
26752 parent.appendChild(element);
26754 // The next element is not a group so insert the new one
26755 parent.insertBefore(element, current);
26762 function removeExcessElements(current) {
26765 next = current.nextSibling;
26766 jqLiteRemove(current);
26772 function skipEmptyAndUnknownOptions(current) {
26773 var emptyOption_ = emptyOption && emptyOption[0];
26774 var unknownOption_ = unknownOption && unknownOption[0];
26776 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26777 // because the compiled empty option might have been replaced by a comment because
26778 // it had an "element" transclusion directive on it (such as ngIf)
26779 if (emptyOption_ || unknownOption_) {
26781 (current === emptyOption_ ||
26782 current === unknownOption_ ||
26783 current.nodeType === NODE_TYPE_COMMENT ||
26784 current.value === '')) {
26785 current = current.nextSibling;
26792 function updateOptions() {
26794 var previousValue = options && selectCtrl.readValue();
26796 options = ngOptions.getOptions();
26799 var currentElement = selectElement[0].firstChild;
26801 // Ensure that the empty option is always there if it was explicitly provided
26802 if (providedEmptyOption) {
26803 selectElement.prepend(emptyOption);
26806 currentElement = skipEmptyAndUnknownOptions(currentElement);
26808 options.items.forEach(function updateOption(option) {
26813 if (option.group) {
26815 // This option is to live in a group
26816 // See if we have already created this group
26817 group = groupMap[option.group];
26821 // We have not already created this group
26822 groupElement = addOrReuseElement(selectElement[0],
26826 // Move to the next element
26827 currentElement = groupElement.nextSibling;
26829 // Update the label on the group element
26830 groupElement.label = option.group;
26832 // Store it for use later
26833 group = groupMap[option.group] = {
26834 groupElement: groupElement,
26835 currentOptionElement: groupElement.firstChild
26840 // So now we have a group for this option we add the option to the group
26841 optionElement = addOrReuseElement(group.groupElement,
26842 group.currentOptionElement,
26845 updateOptionElement(option, optionElement);
26846 // Move to the next element
26847 group.currentOptionElement = optionElement.nextSibling;
26851 // This option is not in a group
26852 optionElement = addOrReuseElement(selectElement[0],
26856 updateOptionElement(option, optionElement);
26857 // Move to the next element
26858 currentElement = optionElement.nextSibling;
26863 // Now remove all excess options and group
26864 Object.keys(groupMap).forEach(function(key) {
26865 removeExcessElements(groupMap[key].currentOptionElement);
26867 removeExcessElements(currentElement);
26869 ngModelCtrl.$render();
26871 // Check to see if the value has changed due to the update to the options
26872 if (!ngModelCtrl.$isEmpty(previousValue)) {
26873 var nextValue = selectCtrl.readValue();
26874 if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26875 ngModelCtrl.$setViewValue(nextValue);
26876 ngModelCtrl.$render();
26886 require: ['select', '?ngModel'],
26888 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26889 // Deactivate the SelectController.register method to prevent
26890 // option directives from accidentally registering themselves
26891 // (and unwanted $destroy handlers etc.)
26892 ctrls[0].registerOption = noop;
26894 post: ngOptionsPostLink
26901 * @name ngPluralize
26905 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26906 * These rules are bundled with angular.js, but can be overridden
26907 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26908 * by specifying the mappings between
26909 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26910 * and the strings to be displayed.
26912 * # Plural categories and explicit number rules
26914 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26915 * in Angular's default en-US locale: "one" and "other".
26917 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26918 * any number that is not 1), an explicit number rule can only match one number. For example, the
26919 * explicit number rule for "3" matches the number 3. There are examples of plural categories
26920 * and explicit number rules throughout the rest of this documentation.
26922 * # Configuring ngPluralize
26923 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26924 * You can also provide an optional attribute, `offset`.
26926 * The value of the `count` attribute can be either a string or an {@link guide/expression
26927 * Angular expression}; these are evaluated on the current scope for its bound value.
26929 * The `when` attribute specifies the mappings between plural categories and the actual
26930 * string to be displayed. The value of the attribute should be a JSON object.
26932 * The following example shows how to configure ngPluralize:
26935 * <ng-pluralize count="personCount"
26936 when="{'0': 'Nobody is viewing.',
26937 * 'one': '1 person is viewing.',
26938 * 'other': '{} people are viewing.'}">
26942 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26943 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26944 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26945 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26946 * show "a dozen people are viewing".
26948 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26949 * into pluralized strings. In the previous example, Angular will replace `{}` with
26950 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26951 * for <span ng-non-bindable>{{numberExpression}}</span>.
26953 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26954 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26956 * # Configuring ngPluralize with offset
26957 * The `offset` attribute allows further customization of pluralized text, which can result in
26958 * a better user experience. For example, instead of the message "4 people are viewing this document",
26959 * you might display "John, Kate and 2 others are viewing this document".
26960 * The offset attribute allows you to offset a number by any desired value.
26961 * Let's take a look at an example:
26964 * <ng-pluralize count="personCount" offset=2
26965 * when="{'0': 'Nobody is viewing.',
26966 * '1': '{{person1}} is viewing.',
26967 * '2': '{{person1}} and {{person2}} are viewing.',
26968 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
26969 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26973 * Notice that we are still using two plural categories(one, other), but we added
26974 * three explicit number rules 0, 1 and 2.
26975 * When one person, perhaps John, views the document, "John is viewing" will be shown.
26976 * When three people view the document, no explicit number rule is found, so
26977 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26978 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26981 * Note that when you specify offsets, you must provide explicit number rules for
26982 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26983 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26984 * plural categories "one" and "other".
26986 * @param {string|expression} count The variable to be bound to.
26987 * @param {string} when The mapping between plural category to its corresponding strings.
26988 * @param {number=} offset Offset to deduct from the total number.
26991 <example module="pluralizeExample">
26992 <file name="index.html">
26994 angular.module('pluralizeExample', [])
26995 .controller('ExampleController', ['$scope', function($scope) {
26996 $scope.person1 = 'Igor';
26997 $scope.person2 = 'Misko';
26998 $scope.personCount = 1;
27001 <div ng-controller="ExampleController">
27002 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27003 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27004 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27006 <!--- Example with simple pluralization rules for en locale --->
27008 <ng-pluralize count="personCount"
27009 when="{'0': 'Nobody is viewing.',
27010 'one': '1 person is viewing.',
27011 'other': '{} people are viewing.'}">
27012 </ng-pluralize><br>
27014 <!--- Example with offset --->
27016 <ng-pluralize count="personCount" offset=2
27017 when="{'0': 'Nobody is viewing.',
27018 '1': '{{person1}} is viewing.',
27019 '2': '{{person1}} and {{person2}} are viewing.',
27020 'one': '{{person1}}, {{person2}} and one other person are viewing.',
27021 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27025 <file name="protractor.js" type="protractor">
27026 it('should show correct pluralized string', function() {
27027 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27028 var withOffset = element.all(by.css('ng-pluralize')).get(1);
27029 var countInput = element(by.model('personCount'));
27031 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27032 expect(withOffset.getText()).toEqual('Igor is viewing.');
27034 countInput.clear();
27035 countInput.sendKeys('0');
27037 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27038 expect(withOffset.getText()).toEqual('Nobody is viewing.');
27040 countInput.clear();
27041 countInput.sendKeys('2');
27043 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27044 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27046 countInput.clear();
27047 countInput.sendKeys('3');
27049 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27050 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27052 countInput.clear();
27053 countInput.sendKeys('4');
27055 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27056 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27058 it('should show data-bound names', function() {
27059 var withOffset = element.all(by.css('ng-pluralize')).get(1);
27060 var personCount = element(by.model('personCount'));
27061 var person1 = element(by.model('person1'));
27062 var person2 = element(by.model('person2'));
27063 personCount.clear();
27064 personCount.sendKeys('4');
27066 person1.sendKeys('Di');
27068 person2.sendKeys('Vojta');
27069 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27074 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27076 IS_WHEN = /^when(Minus)?(.+)$/;
27079 link: function(scope, element, attr) {
27080 var numberExp = attr.count,
27081 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27082 offset = attr.offset || 0,
27083 whens = scope.$eval(whenExp) || {},
27085 startSymbol = $interpolate.startSymbol(),
27086 endSymbol = $interpolate.endSymbol(),
27087 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27088 watchRemover = angular.noop,
27091 forEach(attr, function(expression, attributeName) {
27092 var tmpMatch = IS_WHEN.exec(attributeName);
27094 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27095 whens[whenKey] = element.attr(attr.$attr[attributeName]);
27098 forEach(whens, function(expression, key) {
27099 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27103 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27104 var count = parseFloat(newVal);
27105 var countIsNaN = isNaN(count);
27107 if (!countIsNaN && !(count in whens)) {
27108 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27109 // Otherwise, check it against pluralization rules in $locale service.
27110 count = $locale.pluralCat(count - offset);
27113 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27114 // In JS `NaN !== NaN`, so we have to exlicitly check.
27115 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27117 var whenExpFn = whensExpFns[count];
27118 if (isUndefined(whenExpFn)) {
27119 if (newVal != null) {
27120 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27122 watchRemover = noop;
27123 updateElementText();
27125 watchRemover = scope.$watch(whenExpFn, updateElementText);
27131 function updateElementText(newText) {
27132 element.text(newText || '');
27144 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27145 * instance gets its own scope, where the given loop variable is set to the current collection item,
27146 * and `$index` is set to the item index or key.
27148 * Special properties are exposed on the local scope of each template instance, including:
27150 * | Variable | Type | Details |
27151 * |-----------|-----------------|-----------------------------------------------------------------------------|
27152 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
27153 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
27154 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27155 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
27156 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
27157 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
27159 * <div class="alert alert-info">
27160 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27161 * This may be useful when, for instance, nesting ngRepeats.
27165 * # Iterating over object properties
27167 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27171 * <div ng-repeat="(key, value) in myObj"> ... </div>
27174 * You need to be aware that the JavaScript specification does not define the order of keys
27175 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27176 * used to sort the keys alphabetically.)
27178 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27179 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27180 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27181 * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27183 * If this is not desired, the recommended workaround is to convert your object into an array
27184 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
27185 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27186 * or implement a `$watch` on the object yourself.
27189 * # Tracking and Duplicates
27191 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27192 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27194 * * When an item is added, a new instance of the template is added to the DOM.
27195 * * When an item is removed, its template instance is removed from the DOM.
27196 * * When items are reordered, their respective templates are reordered in the DOM.
27198 * To minimize creation of DOM elements, `ngRepeat` uses a function
27199 * to "keep track" of all items in the collection and their corresponding DOM elements.
27200 * For example, if an item is added to the collection, ngRepeat will know that all other items
27201 * already have DOM elements, and will not re-render them.
27203 * The default tracking function (which tracks items by their identity) does not allow
27204 * duplicate items in arrays. This is because when there are duplicates, it is not possible
27205 * to maintain a one-to-one mapping between collection items and DOM elements.
27207 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27208 * with your own using the `track by` expression.
27210 * For example, you may track items by the index of each item in the collection, using the
27211 * special scope property `$index`:
27213 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27218 * You may also use arbitrary expressions in `track by`, including references to custom functions
27221 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27226 * <div class="alert alert-success">
27227 * If you are working with objects that have an identifier property, you should track
27228 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27229 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27230 * JavaScript objects in the collection have been substituted for new ones. For large collections,
27231 * this signifincantly improves rendering performance. If you don't have a unique identifier,
27232 * `track by $index` can also provide a performance boost.
27235 * <div ng-repeat="model in collection track by model.id">
27240 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27241 * `$id` function, which tracks items by their identity:
27243 * <div ng-repeat="obj in collection track by $id(obj)">
27248 * <div class="alert alert-warning">
27249 * **Note:** `track by` must always be the last expression:
27252 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27257 * # Special repeat start and end points
27258 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27259 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27260 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
27261 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27263 * The example below makes use of this feature:
27265 * <header ng-repeat-start="item in items">
27266 * Header {{ item }}
27268 * <div class="body">
27271 * <footer ng-repeat-end>
27272 * Footer {{ item }}
27276 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27281 * <div class="body">
27290 * <div class="body">
27298 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27299 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27302 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27304 * **.leave** - when an item is removed from the list or when an item is filtered out
27306 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27311 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27312 * formats are currently supported:
27314 * * `variable in expression` – where variable is the user defined loop variable and `expression`
27315 * is a scope expression giving the collection to enumerate.
27317 * For example: `album in artist.albums`.
27319 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27320 * and `expression` is the scope expression giving the collection to enumerate.
27322 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
27324 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27325 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27326 * is specified, ng-repeat associates elements by identity. It is an error to have
27327 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27328 * mapped to the same DOM element, which is not possible.)
27330 * Note that the tracking expression must come last, after any filters, and the alias expression.
27332 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27333 * will be associated by item identity in the array.
27335 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27336 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27337 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27338 * element in the same way in the DOM.
27340 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27341 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27342 * property is same.
27344 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27345 * to items in conjunction with a tracking expression.
27347 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27348 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27349 * when a filter is active on the repeater, but the filtered result set is empty.
27351 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27352 * the items have been processed through the filter.
27354 * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
27355 * (and not as operator, inside an expression).
27357 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27360 * This example initializes the scope to a list of names and
27361 * then uses `ngRepeat` to display every person:
27362 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27363 <file name="index.html">
27364 <div ng-init="friends = [
27365 {name:'John', age:25, gender:'boy'},
27366 {name:'Jessie', age:30, gender:'girl'},
27367 {name:'Johanna', age:28, gender:'girl'},
27368 {name:'Joy', age:15, gender:'girl'},
27369 {name:'Mary', age:28, gender:'girl'},
27370 {name:'Peter', age:95, gender:'boy'},
27371 {name:'Sebastian', age:50, gender:'boy'},
27372 {name:'Erika', age:27, gender:'girl'},
27373 {name:'Patrick', age:40, gender:'boy'},
27374 {name:'Samantha', age:60, gender:'girl'}
27376 I have {{friends.length}} friends. They are:
27377 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27378 <ul class="example-animate-container">
27379 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27380 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27382 <li class="animate-repeat" ng-if="results.length == 0">
27383 <strong>No results found...</strong>
27388 <file name="animations.css">
27389 .example-animate-container {
27391 border:1px solid black;
27400 box-sizing:border-box;
27403 .animate-repeat.ng-move,
27404 .animate-repeat.ng-enter,
27405 .animate-repeat.ng-leave {
27406 transition:all linear 0.5s;
27409 .animate-repeat.ng-leave.ng-leave-active,
27410 .animate-repeat.ng-move,
27411 .animate-repeat.ng-enter {
27416 .animate-repeat.ng-leave,
27417 .animate-repeat.ng-move.ng-move-active,
27418 .animate-repeat.ng-enter.ng-enter-active {
27423 <file name="protractor.js" type="protractor">
27424 var friends = element.all(by.repeater('friend in friends'));
27426 it('should render initial data set', function() {
27427 expect(friends.count()).toBe(10);
27428 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27429 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27430 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27431 expect(element(by.binding('friends.length')).getText())
27432 .toMatch("I have 10 friends. They are:");
27435 it('should update repeater when filter predicate changes', function() {
27436 expect(friends.count()).toBe(10);
27438 element(by.model('q')).sendKeys('ma');
27440 expect(friends.count()).toBe(2);
27441 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27442 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27447 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27448 var NG_REMOVED = '$$NG_REMOVED';
27449 var ngRepeatMinErr = minErr('ngRepeat');
27451 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27452 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27453 scope[valueIdentifier] = value;
27454 if (keyIdentifier) scope[keyIdentifier] = key;
27455 scope.$index = index;
27456 scope.$first = (index === 0);
27457 scope.$last = (index === (arrayLength - 1));
27458 scope.$middle = !(scope.$first || scope.$last);
27459 // jshint bitwise: false
27460 scope.$odd = !(scope.$even = (index&1) === 0);
27461 // jshint bitwise: true
27464 var getBlockStart = function(block) {
27465 return block.clone[0];
27468 var getBlockEnd = function(block) {
27469 return block.clone[block.clone.length - 1];
27475 multiElement: true,
27476 transclude: 'element',
27480 compile: function ngRepeatCompile($element, $attr) {
27481 var expression = $attr.ngRepeat;
27482 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27484 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
27487 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27491 var lhs = match[1];
27492 var rhs = match[2];
27493 var aliasAs = match[3];
27494 var trackByExp = match[4];
27496 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27499 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27502 var valueIdentifier = match[3] || match[1];
27503 var keyIdentifier = match[2];
27505 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27506 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27507 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27511 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27512 var hashFnLocals = {$id: hashKey};
27515 trackByExpGetter = $parse(trackByExp);
27517 trackByIdArrayFn = function(key, value) {
27518 return hashKey(value);
27520 trackByIdObjFn = function(key) {
27525 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27527 if (trackByExpGetter) {
27528 trackByIdExpFn = function(key, value, index) {
27529 // assign key, value, and $index to the locals so that they can be used in hash functions
27530 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27531 hashFnLocals[valueIdentifier] = value;
27532 hashFnLocals.$index = index;
27533 return trackByExpGetter($scope, hashFnLocals);
27537 // Store a list of elements from previous run. This is a hash where key is the item from the
27538 // iterator, and the value is objects with following properties.
27539 // - scope: bound scope
27540 // - element: previous element.
27541 // - index: position
27543 // We are using no-proto object so that we don't need to guard against inherited props via
27545 var lastBlockMap = createMap();
27548 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27550 previousNode = $element[0], // node that cloned nodes should be inserted after
27551 // initialized to the comment node anchor
27553 // Same as lastBlockMap but it has the current state. It will become the
27554 // lastBlockMap on the next iteration.
27555 nextBlockMap = createMap(),
27557 key, value, // key/value of iteration
27561 block, // last object information {scope, element, id}
27566 $scope[aliasAs] = collection;
27569 if (isArrayLike(collection)) {
27570 collectionKeys = collection;
27571 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27573 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27574 // if object, extract keys, in enumeration order, unsorted
27575 collectionKeys = [];
27576 for (var itemKey in collection) {
27577 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27578 collectionKeys.push(itemKey);
27583 collectionLength = collectionKeys.length;
27584 nextBlockOrder = new Array(collectionLength);
27586 // locate existing items
27587 for (index = 0; index < collectionLength; index++) {
27588 key = (collection === collectionKeys) ? index : collectionKeys[index];
27589 value = collection[key];
27590 trackById = trackByIdFn(key, value, index);
27591 if (lastBlockMap[trackById]) {
27592 // found previously seen block
27593 block = lastBlockMap[trackById];
27594 delete lastBlockMap[trackById];
27595 nextBlockMap[trackById] = block;
27596 nextBlockOrder[index] = block;
27597 } else if (nextBlockMap[trackById]) {
27598 // if collision detected. restore lastBlockMap and throw an error
27599 forEach(nextBlockOrder, function(block) {
27600 if (block && block.scope) lastBlockMap[block.id] = block;
27602 throw ngRepeatMinErr('dupes',
27603 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27604 expression, trackById, value);
27606 // new never before seen block
27607 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27608 nextBlockMap[trackById] = true;
27612 // remove leftover items
27613 for (var blockKey in lastBlockMap) {
27614 block = lastBlockMap[blockKey];
27615 elementsToRemove = getBlockNodes(block.clone);
27616 $animate.leave(elementsToRemove);
27617 if (elementsToRemove[0].parentNode) {
27618 // if the element was not removed yet because of pending animation, mark it as deleted
27619 // so that we can ignore it later
27620 for (index = 0, length = elementsToRemove.length; index < length; index++) {
27621 elementsToRemove[index][NG_REMOVED] = true;
27624 block.scope.$destroy();
27627 // we are not using forEach for perf reasons (trying to avoid #call)
27628 for (index = 0; index < collectionLength; index++) {
27629 key = (collection === collectionKeys) ? index : collectionKeys[index];
27630 value = collection[key];
27631 block = nextBlockOrder[index];
27634 // if we have already seen this object, then we need to reuse the
27635 // associated scope/element
27637 nextNode = previousNode;
27639 // skip nodes that are already pending removal via leave animation
27641 nextNode = nextNode.nextSibling;
27642 } while (nextNode && nextNode[NG_REMOVED]);
27644 if (getBlockStart(block) != nextNode) {
27645 // existing item which got moved
27646 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27648 previousNode = getBlockEnd(block);
27649 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27651 // new item which we don't know about
27652 $transclude(function ngRepeatTransclude(clone, scope) {
27653 block.scope = scope;
27654 // http://jsperf.com/clone-vs-createcomment
27655 var endNode = ngRepeatEndComment.cloneNode(false);
27656 clone[clone.length++] = endNode;
27658 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27659 $animate.enter(clone, null, jqLite(previousNode));
27660 previousNode = endNode;
27661 // Note: We only need the first/last node of the cloned nodes.
27662 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27663 // by a directive with templateUrl when its template arrives.
27664 block.clone = clone;
27665 nextBlockMap[block.id] = block;
27666 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27670 lastBlockMap = nextBlockMap;
27677 var NG_HIDE_CLASS = 'ng-hide';
27678 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27685 * The `ngShow` directive shows or hides the given HTML element based on the expression
27686 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27687 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27688 * in AngularJS and sets the display style to none (using an !important flag).
27689 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27692 * <!-- when $scope.myValue is truthy (element is visible) -->
27693 * <div ng-show="myValue"></div>
27695 * <!-- when $scope.myValue is falsy (element is hidden) -->
27696 * <div ng-show="myValue" class="ng-hide"></div>
27699 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27700 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27701 * from the element causing the element not to appear hidden.
27703 * ## Why is !important used?
27705 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27706 * can be easily overridden by heavier selectors. For example, something as simple
27707 * as changing the display style on a HTML list item would make hidden elements appear visible.
27708 * This also becomes a bigger issue when dealing with CSS frameworks.
27710 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27711 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27712 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27714 * ### Overriding `.ng-hide`
27716 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27717 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27718 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27719 * with extra animation classes that can be added.
27722 * .ng-hide:not(.ng-hide-animate) {
27723 * /* this is just another form of hiding an element */
27724 * display: block!important;
27725 * position: absolute;
27731 * By default you don't need to override in CSS anything and the animations will work around the display style.
27733 * ## A note about animations with `ngShow`
27735 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27736 * is true and false. This system works like the animation system present with ngClass except that
27737 * you must also include the !important flag to override the display property
27738 * so that you can perform an animation when the element is hidden during the time of the animation.
27742 * //a working example can be found at the bottom of this page
27744 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27745 * /* this is required as of 1.3x to properly
27746 * apply all styling in a show/hide animation */
27747 * transition: 0s linear all;
27750 * .my-element.ng-hide-add-active,
27751 * .my-element.ng-hide-remove-active {
27752 * /* the transition is defined in the active class */
27753 * transition: 1s linear all;
27756 * .my-element.ng-hide-add { ... }
27757 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27758 * .my-element.ng-hide-remove { ... }
27759 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27762 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27763 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27766 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27767 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27770 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27771 * then the element is shown or hidden respectively.
27774 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27775 <file name="index.html">
27776 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27779 <div class="check-element animate-show" ng-show="checked">
27780 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27785 <div class="check-element animate-show" ng-hide="checked">
27786 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27790 <file name="glyphicons.css">
27791 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27793 <file name="animations.css">
27798 border: 1px solid black;
27802 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27803 transition: all linear 0.5s;
27806 .animate-show.ng-hide {
27814 border: 1px solid black;
27818 <file name="protractor.js" type="protractor">
27819 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27820 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27822 it('should check ng-show / ng-hide', function() {
27823 expect(thumbsUp.isDisplayed()).toBeFalsy();
27824 expect(thumbsDown.isDisplayed()).toBeTruthy();
27826 element(by.model('checked')).click();
27828 expect(thumbsUp.isDisplayed()).toBeTruthy();
27829 expect(thumbsDown.isDisplayed()).toBeFalsy();
27834 var ngShowDirective = ['$animate', function($animate) {
27837 multiElement: true,
27838 link: function(scope, element, attr) {
27839 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27840 // we're adding a temporary, animation-specific class for ng-hide since this way
27841 // we can control when the element is actually displayed on screen without having
27842 // to have a global/greedy CSS selector that breaks when other animations are run.
27843 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27844 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27845 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27859 * The `ngHide` directive shows or hides the given HTML element based on the expression
27860 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27861 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27862 * in AngularJS and sets the display style to none (using an !important flag).
27863 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27866 * <!-- when $scope.myValue is truthy (element is hidden) -->
27867 * <div ng-hide="myValue" class="ng-hide"></div>
27869 * <!-- when $scope.myValue is falsy (element is visible) -->
27870 * <div ng-hide="myValue"></div>
27873 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27874 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27875 * from the element causing the element not to appear hidden.
27877 * ## Why is !important used?
27879 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27880 * can be easily overridden by heavier selectors. For example, something as simple
27881 * as changing the display style on a HTML list item would make hidden elements appear visible.
27882 * This also becomes a bigger issue when dealing with CSS frameworks.
27884 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27885 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27886 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27888 * ### Overriding `.ng-hide`
27890 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27891 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27896 * /* this is just another form of hiding an element */
27897 * display: block!important;
27898 * position: absolute;
27904 * By default you don't need to override in CSS anything and the animations will work around the display style.
27906 * ## A note about animations with `ngHide`
27908 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27909 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27910 * CSS class is added and removed for you instead of your own CSS class.
27914 * //a working example can be found at the bottom of this page
27916 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27917 * transition: 0.5s linear all;
27920 * .my-element.ng-hide-add { ... }
27921 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27922 * .my-element.ng-hide-remove { ... }
27923 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27926 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27927 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27930 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27931 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27934 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27935 * the element is shown or hidden respectively.
27938 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27939 <file name="index.html">
27940 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27943 <div class="check-element animate-hide" ng-show="checked">
27944 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27949 <div class="check-element animate-hide" ng-hide="checked">
27950 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27954 <file name="glyphicons.css">
27955 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27957 <file name="animations.css">
27959 transition: all linear 0.5s;
27963 border: 1px solid black;
27967 .animate-hide.ng-hide {
27975 border: 1px solid black;
27979 <file name="protractor.js" type="protractor">
27980 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27981 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27983 it('should check ng-show / ng-hide', function() {
27984 expect(thumbsUp.isDisplayed()).toBeFalsy();
27985 expect(thumbsDown.isDisplayed()).toBeTruthy();
27987 element(by.model('checked')).click();
27989 expect(thumbsUp.isDisplayed()).toBeTruthy();
27990 expect(thumbsDown.isDisplayed()).toBeFalsy();
27995 var ngHideDirective = ['$animate', function($animate) {
27998 multiElement: true,
27999 link: function(scope, element, attr) {
28000 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28001 // The comment inside of the ngShowDirective explains why we add and
28002 // remove a temporary class for the show/hide animation
28003 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28004 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28017 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28020 * @param {expression} ngStyle
28022 * {@link guide/expression Expression} which evals to an
28023 * object whose keys are CSS style names and values are corresponding values for those CSS
28026 * Since some CSS style names are not valid keys for an object, they must be quoted.
28027 * See the 'background-color' style in the example below.
28031 <file name="index.html">
28032 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28033 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28034 <input type="button" value="clear" ng-click="myStyle={}">
28036 <span ng-style="myStyle">Sample Text</span>
28037 <pre>myStyle={{myStyle}}</pre>
28039 <file name="style.css">
28044 <file name="protractor.js" type="protractor">
28045 var colorSpan = element(by.css('span'));
28047 it('should check ng-style', function() {
28048 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28049 element(by.css('input[value=\'set color\']')).click();
28050 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28051 element(by.css('input[value=clear]')).click();
28052 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28057 var ngStyleDirective = ngDirective(function(scope, element, attr) {
28058 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28059 if (oldStyles && (newStyles !== oldStyles)) {
28060 forEach(oldStyles, function(val, style) { element.css(style, '');});
28062 if (newStyles) element.css(newStyles);
28072 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28073 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28074 * as specified in the template.
28076 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28077 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28078 * matches the value obtained from the evaluated expression. In other words, you define a container element
28079 * (where you place the directive), place an expression on the **`on="..."` attribute**
28080 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28081 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28082 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28083 * attribute is displayed.
28085 * <div class="alert alert-info">
28086 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28087 * as literal string values to match against.
28088 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28089 * value of the expression `$scope.someVal`.
28093 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28094 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28099 * <ANY ng-switch="expression">
28100 * <ANY ng-switch-when="matchValue1">...</ANY>
28101 * <ANY ng-switch-when="matchValue2">...</ANY>
28102 * <ANY ng-switch-default>...</ANY>
28109 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28110 * On child elements add:
28112 * * `ngSwitchWhen`: the case statement to match against. If match then this
28113 * case will be displayed. If the same match appears multiple times, all the
28114 * elements will be displayed.
28115 * * `ngSwitchDefault`: the default case when no other case match. If there
28116 * are multiple default cases, all of them will be displayed when no other
28121 <example module="switchExample" deps="angular-animate.js" animations="true">
28122 <file name="index.html">
28123 <div ng-controller="ExampleController">
28124 <select ng-model="selection" ng-options="item for item in items">
28126 <code>selection={{selection}}</code>
28128 <div class="animate-switch-container"
28129 ng-switch on="selection">
28130 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28131 <div class="animate-switch" ng-switch-when="home">Home Span</div>
28132 <div class="animate-switch" ng-switch-default>default</div>
28136 <file name="script.js">
28137 angular.module('switchExample', ['ngAnimate'])
28138 .controller('ExampleController', ['$scope', function($scope) {
28139 $scope.items = ['settings', 'home', 'other'];
28140 $scope.selection = $scope.items[0];
28143 <file name="animations.css">
28144 .animate-switch-container {
28147 border:1px solid black;
28156 .animate-switch.ng-animate {
28157 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28166 .animate-switch.ng-leave.ng-leave-active,
28167 .animate-switch.ng-enter {
28170 .animate-switch.ng-leave,
28171 .animate-switch.ng-enter.ng-enter-active {
28175 <file name="protractor.js" type="protractor">
28176 var switchElem = element(by.css('[ng-switch]'));
28177 var select = element(by.model('selection'));
28179 it('should start in settings', function() {
28180 expect(switchElem.getText()).toMatch(/Settings Div/);
28182 it('should change to home', function() {
28183 select.all(by.css('option')).get(1).click();
28184 expect(switchElem.getText()).toMatch(/Home Span/);
28186 it('should select default', function() {
28187 select.all(by.css('option')).get(2).click();
28188 expect(switchElem.getText()).toMatch(/default/);
28193 var ngSwitchDirective = ['$animate', function($animate) {
28195 require: 'ngSwitch',
28197 // asks for $scope to fool the BC controller module
28198 controller: ['$scope', function ngSwitchController() {
28201 link: function(scope, element, attr, ngSwitchController) {
28202 var watchExpr = attr.ngSwitch || attr.on,
28203 selectedTranscludes = [],
28204 selectedElements = [],
28205 previousLeaveAnimations = [],
28206 selectedScopes = [];
28208 var spliceFactory = function(array, index) {
28209 return function() { array.splice(index, 1); };
28212 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28214 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28215 $animate.cancel(previousLeaveAnimations[i]);
28217 previousLeaveAnimations.length = 0;
28219 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28220 var selected = getBlockNodes(selectedElements[i].clone);
28221 selectedScopes[i].$destroy();
28222 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28223 promise.then(spliceFactory(previousLeaveAnimations, i));
28226 selectedElements.length = 0;
28227 selectedScopes.length = 0;
28229 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28230 forEach(selectedTranscludes, function(selectedTransclude) {
28231 selectedTransclude.transclude(function(caseElement, selectedScope) {
28232 selectedScopes.push(selectedScope);
28233 var anchor = selectedTransclude.element;
28234 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28235 var block = { clone: caseElement };
28237 selectedElements.push(block);
28238 $animate.enter(caseElement, anchor.parent(), anchor);
28247 var ngSwitchWhenDirective = ngDirective({
28248 transclude: 'element',
28250 require: '^ngSwitch',
28251 multiElement: true,
28252 link: function(scope, element, attrs, ctrl, $transclude) {
28253 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28254 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28258 var ngSwitchDefaultDirective = ngDirective({
28259 transclude: 'element',
28261 require: '^ngSwitch',
28262 multiElement: true,
28263 link: function(scope, element, attr, ctrl, $transclude) {
28264 ctrl.cases['?'] = (ctrl.cases['?'] || []);
28265 ctrl.cases['?'].push({ transclude: $transclude, element: element });
28271 * @name ngTransclude
28275 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28277 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28282 <example module="transcludeExample">
28283 <file name="index.html">
28285 angular.module('transcludeExample', [])
28286 .directive('pane', function(){
28290 scope: { title:'@' },
28291 template: '<div style="border: 1px solid black;">' +
28292 '<div style="background-color: gray">{{title}}</div>' +
28293 '<ng-transclude></ng-transclude>' +
28297 .controller('ExampleController', ['$scope', function($scope) {
28298 $scope.title = 'Lorem Ipsum';
28299 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28302 <div ng-controller="ExampleController">
28303 <input ng-model="title" aria-label="title"> <br/>
28304 <textarea ng-model="text" aria-label="text"></textarea> <br/>
28305 <pane title="{{title}}">{{text}}</pane>
28308 <file name="protractor.js" type="protractor">
28309 it('should have transcluded', function() {
28310 var titleElement = element(by.model('title'));
28311 titleElement.clear();
28312 titleElement.sendKeys('TITLE');
28313 var textElement = element(by.model('text'));
28314 textElement.clear();
28315 textElement.sendKeys('TEXT');
28316 expect(element(by.binding('title')).getText()).toEqual('TITLE');
28317 expect(element(by.binding('text')).getText()).toEqual('TEXT');
28323 var ngTranscludeDirective = ngDirective({
28325 link: function($scope, $element, $attrs, controller, $transclude) {
28326 if (!$transclude) {
28327 throw minErr('ngTransclude')('orphan',
28328 'Illegal use of ngTransclude directive in the template! ' +
28329 'No parent directive that requires a transclusion found. ' +
28331 startingTag($element));
28334 $transclude(function(clone) {
28336 $element.append(clone);
28347 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28348 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28349 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28350 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28351 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28353 * @param {string} type Must be set to `'text/ng-template'`.
28354 * @param {string} id Cache name of the template.
28358 <file name="index.html">
28359 <script type="text/ng-template" id="/tpl.html">
28360 Content of the template.
28363 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28364 <div id="tpl-content" ng-include src="currentTpl"></div>
28366 <file name="protractor.js" type="protractor">
28367 it('should load template defined inside script tag', function() {
28368 element(by.css('#tpl-link')).click();
28369 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28374 var scriptDirective = ['$templateCache', function($templateCache) {
28378 compile: function(element, attr) {
28379 if (attr.type == 'text/ng-template') {
28380 var templateUrl = attr.id,
28381 text = element[0].text;
28383 $templateCache.put(templateUrl, text);
28389 var noopNgModelController = { $setViewValue: noop, $render: noop };
28391 function chromeHack(optionElement) {
28392 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28393 // Adding an <option selected="selected"> element to a <select required="required"> should
28394 // automatically select the new element
28395 if (optionElement[0].hasAttribute('selected')) {
28396 optionElement[0].selected = true;
28402 * @name select.SelectController
28404 * The controller for the `<select>` directive. This provides support for reading
28405 * and writing the selected value(s) of the control and also coordinates dynamically
28406 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28408 var SelectController =
28409 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28412 optionsMap = new HashMap();
28414 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28415 self.ngModelCtrl = noopNgModelController;
28417 // The "unknown" option is one that is prepended to the list if the viewValue
28418 // does not match any of the options. When it is rendered the value of the unknown
28419 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28421 // We can't just jqLite('<option>') since jqLite is not smart enough
28422 // to create it in <select> and IE barfs otherwise.
28423 self.unknownOption = jqLite(document.createElement('option'));
28424 self.renderUnknownOption = function(val) {
28425 var unknownVal = '? ' + hashKey(val) + ' ?';
28426 self.unknownOption.val(unknownVal);
28427 $element.prepend(self.unknownOption);
28428 $element.val(unknownVal);
28431 $scope.$on('$destroy', function() {
28432 // disable unknown option so that we don't do work when the whole select is being destroyed
28433 self.renderUnknownOption = noop;
28436 self.removeUnknownOption = function() {
28437 if (self.unknownOption.parent()) self.unknownOption.remove();
28441 // Read the value of the select control, the implementation of this changes depending
28442 // upon whether the select can have multiple values and whether ngOptions is at work.
28443 self.readValue = function readSingleValue() {
28444 self.removeUnknownOption();
28445 return $element.val();
28449 // Write the value to the select control, the implementation of this changes depending
28450 // upon whether the select can have multiple values and whether ngOptions is at work.
28451 self.writeValue = function writeSingleValue(value) {
28452 if (self.hasOption(value)) {
28453 self.removeUnknownOption();
28454 $element.val(value);
28455 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28457 if (value == null && self.emptyOption) {
28458 self.removeUnknownOption();
28461 self.renderUnknownOption(value);
28467 // Tell the select control that an option, with the given value, has been added
28468 self.addOption = function(value, element) {
28469 assertNotHasOwnProperty(value, '"option value"');
28470 if (value === '') {
28471 self.emptyOption = element;
28473 var count = optionsMap.get(value) || 0;
28474 optionsMap.put(value, count + 1);
28475 self.ngModelCtrl.$render();
28476 chromeHack(element);
28479 // Tell the select control that an option, with the given value, has been removed
28480 self.removeOption = function(value) {
28481 var count = optionsMap.get(value);
28484 optionsMap.remove(value);
28485 if (value === '') {
28486 self.emptyOption = undefined;
28489 optionsMap.put(value, count - 1);
28494 // Check whether the select control has an option matching the given value
28495 self.hasOption = function(value) {
28496 return !!optionsMap.get(value);
28500 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28502 if (interpolateValueFn) {
28503 // The value attribute is interpolated
28505 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28506 if (isDefined(oldVal)) {
28507 self.removeOption(oldVal);
28510 self.addOption(newVal, optionElement);
28512 } else if (interpolateTextFn) {
28513 // The text content is interpolated
28514 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28515 optionAttrs.$set('value', newVal);
28516 if (oldVal !== newVal) {
28517 self.removeOption(oldVal);
28519 self.addOption(newVal, optionElement);
28522 // The value attribute is static
28523 self.addOption(optionAttrs.value, optionElement);
28526 optionElement.on('$destroy', function() {
28527 self.removeOption(optionAttrs.value);
28528 self.ngModelCtrl.$render();
28539 * HTML `SELECT` element with angular data-binding.
28541 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28542 * between the scope and the `<select>` control (including setting default values).
28543 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28544 * {@link ngOptions `ngOptions`} directives.
28546 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28547 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28548 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28549 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28551 * <div class="alert alert-warning">
28552 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28553 * When the model needs to be bound to a non-string value, you must either explictly convert it
28554 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28555 * This is because an option element can only be bound to string values at present.
28558 * If the viewValue of `ngModel` does not match any of the options, then the control
28559 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28561 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28562 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28563 * option. See example below for demonstration.
28565 * <div class="alert alert-info">
28566 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28567 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28568 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28569 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28570 * a new scope for each repeated instance.
28574 * @param {string} ngModel Assignable angular expression to data-bind to.
28575 * @param {string=} name Property name of the form under which the control is published.
28576 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28577 * bound to the model as an array.
28578 * @param {string=} required Sets `required` validation error key if the value is not entered.
28579 * @param {string=} ngRequired Adds required attribute and required validation constraint to
28580 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28581 * when you want to data-bind to the required attribute.
28582 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28583 * interaction with the select element.
28584 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28585 * set on the model on selection. See {@link ngOptions `ngOptions`}.
28588 * ### Simple `select` elements with static options
28590 * <example name="static-select" module="staticSelect">
28591 * <file name="index.html">
28592 * <div ng-controller="ExampleController">
28593 * <form name="myForm">
28594 * <label for="singleSelect"> Single select: </label><br>
28595 * <select name="singleSelect" ng-model="data.singleSelect">
28596 * <option value="option-1">Option 1</option>
28597 * <option value="option-2">Option 2</option>
28600 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28601 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28602 * <option value="">---Please select---</option> <!-- not selected / blank option -->
28603 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28604 * <option value="option-2">Option 2</option>
28606 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28607 * <tt>singleSelect = {{data.singleSelect}}</tt>
28610 * <label for="multipleSelect"> Multiple select: </label><br>
28611 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28612 * <option value="option-1">Option 1</option>
28613 * <option value="option-2">Option 2</option>
28614 * <option value="option-3">Option 3</option>
28616 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28620 * <file name="app.js">
28621 * angular.module('staticSelect', [])
28622 * .controller('ExampleController', ['$scope', function($scope) {
28624 * singleSelect: null,
28625 * multipleSelect: [],
28626 * option1: 'option-1',
28629 * $scope.forceUnknownOption = function() {
28630 * $scope.data.singleSelect = 'nonsense';
28636 * ### Using `ngRepeat` to generate `select` options
28637 * <example name="ngrepeat-select" module="ngrepeatSelect">
28638 * <file name="index.html">
28639 * <div ng-controller="ExampleController">
28640 * <form name="myForm">
28641 * <label for="repeatSelect"> Repeat select: </label>
28642 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28643 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28647 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28650 * <file name="app.js">
28651 * angular.module('ngrepeatSelect', [])
28652 * .controller('ExampleController', ['$scope', function($scope) {
28654 * repeatSelect: null,
28655 * availableOptions: [
28656 * {id: '1', name: 'Option A'},
28657 * {id: '2', name: 'Option B'},
28658 * {id: '3', name: 'Option C'}
28666 * ### Using `select` with `ngOptions` and setting a default value
28667 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28669 * <example name="select-with-default-values" module="defaultValueSelect">
28670 * <file name="index.html">
28671 * <div ng-controller="ExampleController">
28672 * <form name="myForm">
28673 * <label for="mySelect">Make a choice:</label>
28674 * <select name="mySelect" id="mySelect"
28675 * ng-options="option.name for option in data.availableOptions track by option.id"
28676 * ng-model="data.selectedOption"></select>
28679 * <tt>option = {{data.selectedOption}}</tt><br/>
28682 * <file name="app.js">
28683 * angular.module('defaultValueSelect', [])
28684 * .controller('ExampleController', ['$scope', function($scope) {
28686 * availableOptions: [
28687 * {id: '1', name: 'Option A'},
28688 * {id: '2', name: 'Option B'},
28689 * {id: '3', name: 'Option C'}
28691 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28698 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28700 * <example name="select-with-non-string-options" module="nonStringSelect">
28701 * <file name="index.html">
28702 * <select ng-model="model.id" convert-to-number>
28703 * <option value="0">Zero</option>
28704 * <option value="1">One</option>
28705 * <option value="2">Two</option>
28709 * <file name="app.js">
28710 * angular.module('nonStringSelect', [])
28711 * .run(function($rootScope) {
28712 * $rootScope.model = { id: 2 };
28714 * .directive('convertToNumber', function() {
28716 * require: 'ngModel',
28717 * link: function(scope, element, attrs, ngModel) {
28718 * ngModel.$parsers.push(function(val) {
28719 * return parseInt(val, 10);
28721 * ngModel.$formatters.push(function(val) {
28728 * <file name="protractor.js" type="protractor">
28729 * it('should initialize to model', function() {
28730 * var select = element(by.css('select'));
28731 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28737 var selectDirective = function() {
28741 require: ['select', '?ngModel'],
28742 controller: SelectController,
28749 function selectPreLink(scope, element, attr, ctrls) {
28751 // if ngModel is not defined, we don't need to do anything
28752 var ngModelCtrl = ctrls[1];
28753 if (!ngModelCtrl) return;
28755 var selectCtrl = ctrls[0];
28757 selectCtrl.ngModelCtrl = ngModelCtrl;
28759 // We delegate rendering to the `writeValue` method, which can be changed
28760 // if the select can have multiple selected values or if the options are being
28761 // generated by `ngOptions`
28762 ngModelCtrl.$render = function() {
28763 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28766 // When the selected item(s) changes we delegate getting the value of the select control
28767 // to the `readValue` method, which can be changed if the select can have multiple
28768 // selected values or if the options are being generated by `ngOptions`
28769 element.on('change', function() {
28770 scope.$apply(function() {
28771 ngModelCtrl.$setViewValue(selectCtrl.readValue());
28775 // If the select allows multiple values then we need to modify how we read and write
28776 // values from and to the control; also what it means for the value to be empty and
28777 // we have to add an extra watch since ngModel doesn't work well with arrays - it
28778 // doesn't trigger rendering if only an item in the array changes.
28779 if (attr.multiple) {
28781 // Read value now needs to check each option to see if it is selected
28782 selectCtrl.readValue = function readMultipleValue() {
28784 forEach(element.find('option'), function(option) {
28785 if (option.selected) {
28786 array.push(option.value);
28792 // Write value now needs to set the selected property of each matching option
28793 selectCtrl.writeValue = function writeMultipleValue(value) {
28794 var items = new HashMap(value);
28795 forEach(element.find('option'), function(option) {
28796 option.selected = isDefined(items.get(option.value));
28800 // we have to do it on each watch since ngModel watches reference, but
28801 // we need to work of an array, so we need to see if anything was inserted/removed
28802 var lastView, lastViewRef = NaN;
28803 scope.$watch(function selectMultipleWatch() {
28804 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28805 lastView = shallowCopy(ngModelCtrl.$viewValue);
28806 ngModelCtrl.$render();
28808 lastViewRef = ngModelCtrl.$viewValue;
28811 // If we are a multiple select then value is now a collection
28812 // so the meaning of $isEmpty changes
28813 ngModelCtrl.$isEmpty = function(value) {
28814 return !value || value.length === 0;
28822 // The option directive is purely designed to communicate the existence (or lack of)
28823 // of dynamically created (and destroyed) option elements to their containing select
28824 // directive via its controller.
28825 var optionDirective = ['$interpolate', function($interpolate) {
28829 compile: function(element, attr) {
28831 if (isDefined(attr.value)) {
28832 // If the value attribute is defined, check if it contains an interpolation
28833 var interpolateValueFn = $interpolate(attr.value, true);
28835 // If the value attribute is not defined then we fall back to the
28836 // text content of the option element, which may be interpolated
28837 var interpolateTextFn = $interpolate(element.text(), true);
28838 if (!interpolateTextFn) {
28839 attr.$set('value', element.text());
28843 return function(scope, element, attr) {
28845 // This is an optimization over using ^^ since we don't want to have to search
28846 // all the way to the root of the DOM for every single option element
28847 var selectCtrlName = '$selectController',
28848 parent = element.parent(),
28849 selectCtrl = parent.data(selectCtrlName) ||
28850 parent.parent().data(selectCtrlName); // in case we are in optgroup
28853 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28860 var styleDirective = valueFn({
28865 var requiredDirective = function() {
28868 require: '?ngModel',
28869 link: function(scope, elm, attr, ctrl) {
28871 attr.required = true; // force truthy in case we are on non input element
28873 ctrl.$validators.required = function(modelValue, viewValue) {
28874 return !attr.required || !ctrl.$isEmpty(viewValue);
28877 attr.$observe('required', function() {
28885 var patternDirective = function() {
28888 require: '?ngModel',
28889 link: function(scope, elm, attr, ctrl) {
28892 var regexp, patternExp = attr.ngPattern || attr.pattern;
28893 attr.$observe('pattern', function(regex) {
28894 if (isString(regex) && regex.length > 0) {
28895 regex = new RegExp('^' + regex + '$');
28898 if (regex && !regex.test) {
28899 throw minErr('ngPattern')('noregexp',
28900 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28901 regex, startingTag(elm));
28904 regexp = regex || undefined;
28908 ctrl.$validators.pattern = function(modelValue, viewValue) {
28909 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28910 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28917 var maxlengthDirective = function() {
28920 require: '?ngModel',
28921 link: function(scope, elm, attr, ctrl) {
28924 var maxlength = -1;
28925 attr.$observe('maxlength', function(value) {
28926 var intVal = toInt(value);
28927 maxlength = isNaN(intVal) ? -1 : intVal;
28930 ctrl.$validators.maxlength = function(modelValue, viewValue) {
28931 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28937 var minlengthDirective = function() {
28940 require: '?ngModel',
28941 link: function(scope, elm, attr, ctrl) {
28945 attr.$observe('minlength', function(value) {
28946 minlength = toInt(value) || 0;
28949 ctrl.$validators.minlength = function(modelValue, viewValue) {
28950 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28956 if (window.angular.bootstrap) {
28957 //AngularJS is already loaded, so we can return here...
28958 console.log('WARNING: Tried to load angular more than once.');
28962 //try to bind to jquery now so that one can write jqLite(document).ready()
28963 //but we will rebind on bootstrap again.
28966 publishExternalAPI(angular);
28968 angular.module("ngLocale", [], ["$provide", function($provide) {
28969 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28970 function getDecimals(n) {
28972 var i = n.indexOf('.');
28973 return (i == -1) ? 0 : n.length - i - 1;
28976 function getVF(n, opt_precision) {
28977 var v = opt_precision;
28979 if (undefined === v) {
28980 v = Math.min(getDecimals(n), 3);
28983 var base = Math.pow(10, v);
28984 var f = ((n * base) | 0) % base;
28985 return {v: v, f: f};
28988 $provide.value("$locale", {
28989 "DATETIME_FORMATS": {
29011 "FIRSTDAYOFWEEK": 6,
29053 "fullDate": "EEEE, MMMM d, y",
29054 "longDate": "MMMM d, y",
29055 "medium": "MMM d, y h:mm:ss a",
29056 "mediumDate": "MMM d, y",
29057 "mediumTime": "h:mm:ss a",
29058 "short": "M/d/yy h:mm a",
29059 "shortDate": "M/d/yy",
29060 "shortTime": "h:mm a"
29062 "NUMBER_FORMATS": {
29063 "CURRENCY_SYM": "$",
29064 "DECIMAL_SEP": ".",
29084 "negPre": "-\u00a4",
29086 "posPre": "\u00a4",
29092 "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
29096 jqLite(document).ready(function() {
29097 angularInit(document, bootstrap);
29100 })(window, document);
29102 !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
29106 /***/ function(module, exports) {
29109 * State-based routing for AngularJS
29114 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29115 * @link http://angular-ui.github.com/
29116 * @license MIT License, http://www.opensource.org/licenses/MIT
29119 /* commonjs package manager support (eg componentjs) */
29120 if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
29121 module.exports = 'ui.router';
29124 (function (window, angular, undefined) {
29125 /*jshint globalstrict:true*/
29126 /*global angular:false*/
29129 var isDefined = angular.isDefined,
29130 isFunction = angular.isFunction,
29131 isString = angular.isString,
29132 isObject = angular.isObject,
29133 isArray = angular.isArray,
29134 forEach = angular.forEach,
29135 extend = angular.extend,
29137 copy = angular.copy,
29138 toJson = angular.toJson;
29140 copy = angular.copy;
29141 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29143 function inherit(parent, extra) {
29144 return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29147 function merge(dst) {
29148 forEach(arguments, function(obj) {
29150 forEach(obj, function(value, key) {
29151 if (!dst.hasOwnProperty(key)) dst[key] = value;
29159 * Finds the common ancestor path between two states.
29161 * @param {Object} first The first state.
29162 * @param {Object} second The second state.
29163 * @return {Array} Returns an array of state names in descending order, not including the root.
29165 function ancestors(first, second) {
29168 for (var n in first.path) {
29169 if (first.path[n] !== second.path[n]) break;
29170 path.push(first.path[n]);
29176 * IE8-safe wrapper for `Object.keys()`.
29178 * @param {Object} object A JavaScript object.
29179 * @return {Array} Returns the keys of the object as an array.
29181 function objectKeys(object) {
29183 return Object.keys(object);
29187 forEach(object, function(val, key) {
29194 * IE8-safe wrapper for `Array.prototype.indexOf()`.
29196 * @param {Array} array A JavaScript array.
29197 * @param {*} value A value to search the array for.
29198 * @return {Number} Returns the array index value of `value`, or `-1` if not present.
29200 function indexOf(array, value) {
29201 if (Array.prototype.indexOf) {
29202 return array.indexOf(value, Number(arguments[2]) || 0);
29204 var len = array.length >>> 0, from = Number(arguments[2]) || 0;
29205 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
29207 if (from < 0) from += len;
29209 for (; from < len; from++) {
29210 if (from in array && array[from] === value) return from;
29216 * Merges a set of parameters with all parameters inherited between the common parents of the
29217 * current state and a given destination state.
29219 * @param {Object} currentParams The value of the current state parameters ($stateParams).
29220 * @param {Object} newParams The set of parameters which will be composited with inherited params.
29221 * @param {Object} $current Internal definition of object representing the current state.
29222 * @param {Object} $to Internal definition of object representing state to transition to.
29224 function inheritParams(currentParams, newParams, $current, $to) {
29225 var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
29227 for (var i in parents) {
29229 if (!parents[i] || !parents[i].params) continue;
29231 if (!parents[i].params) continue;
29232 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29233 parentParams = objectKeys(parents[i].params);
29234 if (!parentParams.length) continue;
29236 for (var j in parentParams) {
29237 if (indexOf(inheritList, parentParams[j]) >= 0) continue;
29238 inheritList.push(parentParams[j]);
29239 inherited[parentParams[j]] = currentParams[parentParams[j]];
29242 return extend({}, inherited, newParams);
29246 * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
29248 * @param {Object} a The first object.
29249 * @param {Object} b The second object.
29250 * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
29251 * it defaults to the list of keys in `a`.
29252 * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
29254 function equalForKeys(a, b, keys) {
29257 for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
29260 for (var i=0; i<keys.length; i++) {
29262 if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
29268 * Returns the subset of an object, based on a list of keys.
29270 * @param {Array} keys
29271 * @param {Object} values
29272 * @return {Boolean} Returns a subset of `values`.
29274 function filterByKeys(keys, values) {
29277 forEach(keys, function (name) {
29278 filtered[name] = values[name];
29284 // when you know that your index values will be unique, or you want last-one-in to win
29285 function indexBy(array, propName) {
29287 forEach(array, function(item) {
29288 result[item[propName]] = item;
29293 // extracted from underscore.js
29294 // Return a copy of the object only containing the whitelisted properties.
29295 function pick(obj) {
29297 var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29298 forEach(keys, function(key) {
29299 if (key in obj) copy[key] = obj[key];
29304 // extracted from underscore.js
29305 // Return a copy of the object omitting the blacklisted properties.
29306 function omit(obj) {
29308 var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29309 for (var key in obj) {
29310 if (indexOf(keys, key) == -1) copy[key] = obj[key];
29315 function pluck(collection, key) {
29316 var result = isArray(collection) ? [] : {};
29318 forEach(collection, function(val, i) {
29319 result[i] = isFunction(key) ? key(val) : val[key];
29324 function filter(collection, callback) {
29325 var array = isArray(collection);
29326 var result = array ? [] : {};
29327 forEach(collection, function(val, i) {
29328 if (callback(val, i)) {
29329 result[array ? result.length : i] = val;
29335 function map(collection, callback) {
29336 var result = isArray(collection) ? [] : {};
29338 forEach(collection, function(val, i) {
29339 result[i] = callback(val, i);
29346 * @name ui.router.util
29349 * # ui.router.util sub-module
29351 * This module is a dependency of other sub-modules. Do not include this module as a dependency
29352 * in your angular app (use {@link ui.router} module instead).
29355 angular.module('ui.router.util', ['ng']);
29359 * @name ui.router.router
29361 * @requires ui.router.util
29364 * # ui.router.router sub-module
29366 * This module is a dependency of other sub-modules. Do not include this module as a dependency
29367 * in your angular app (use {@link ui.router} module instead).
29369 angular.module('ui.router.router', ['ui.router.util']);
29373 * @name ui.router.state
29375 * @requires ui.router.router
29376 * @requires ui.router.util
29379 * # ui.router.state sub-module
29381 * This module is a dependency of the main ui.router module. Do not include this module as a dependency
29382 * in your angular app (use {@link ui.router} module instead).
29385 angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
29391 * @requires ui.router.state
29396 * ## The main module for ui.router
29397 * There are several sub-modules included with the ui.router module, however only this module is needed
29398 * as a dependency within your angular app. The other modules are for organization purposes.
29401 * * ui.router - the main "umbrella" module
29402 * * ui.router.router -
29404 * *You'll need to include **only** this module as the dependency within your angular app.*
29408 * <html ng-app="myApp">
29410 * <script src="js/angular.js"></script>
29411 * <!-- Include the ui-router script -->
29412 * <script src="js/angular-ui-router.min.js"></script>
29414 * // ...and add 'ui.router' as a dependency
29415 * var myApp = angular.module('myApp', ['ui.router']);
29423 angular.module('ui.router', ['ui.router.state']);
29425 angular.module('ui.router.compat', ['ui.router']);
29429 * @name ui.router.util.$resolve
29432 * @requires $injector
29435 * Manages resolution of (acyclic) graphs of promises.
29437 $Resolve.$inject = ['$q', '$injector'];
29438 function $Resolve( $q, $injector) {
29440 var VISIT_IN_PROGRESS = 1,
29443 NO_DEPENDENCIES = [],
29444 NO_LOCALS = NOTHING,
29445 NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
29450 * @name ui.router.util.$resolve#study
29451 * @methodOf ui.router.util.$resolve
29454 * Studies a set of invocables that are likely to be used multiple times.
29456 * $resolve.study(invocables)(locals, parent, self)
29460 * $resolve.resolve(invocables, locals, parent, self)
29462 * but the former is more efficient (in fact `resolve` just calls `study`
29465 * @param {object} invocables Invocable objects
29466 * @return {function} a function to pass in locals, parent and self
29468 this.study = function (invocables) {
29469 if (!isObject(invocables)) throw new Error("'invocables' must be an object");
29470 var invocableKeys = objectKeys(invocables || {});
29472 // Perform a topological sort of invocables to build an ordered plan
29473 var plan = [], cycle = [], visited = {};
29474 function visit(value, key) {
29475 if (visited[key] === VISIT_DONE) return;
29478 if (visited[key] === VISIT_IN_PROGRESS) {
29479 cycle.splice(0, indexOf(cycle, key));
29480 throw new Error("Cyclic dependency: " + cycle.join(" -> "));
29482 visited[key] = VISIT_IN_PROGRESS;
29484 if (isString(value)) {
29485 plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
29487 var params = $injector.annotate(value);
29488 forEach(params, function (param) {
29489 if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
29491 plan.push(key, value, params);
29495 visited[key] = VISIT_DONE;
29497 forEach(invocables, visit);
29498 invocables = cycle = visited = null; // plan is all that's required
29500 function isResolve(value) {
29501 return isObject(value) && value.then && value.$$promises;
29504 return function (locals, parent, self) {
29505 if (isResolve(locals) && self === undefined) {
29506 self = parent; parent = locals; locals = null;
29508 if (!locals) locals = NO_LOCALS;
29509 else if (!isObject(locals)) {
29510 throw new Error("'locals' must be an object");
29512 if (!parent) parent = NO_PARENT;
29513 else if (!isResolve(parent)) {
29514 throw new Error("'parent' must be a promise returned by $resolve.resolve()");
29517 // To complete the overall resolution, we have to wait for the parent
29518 // promise and for the promise for each invokable in our plan.
29519 var resolution = $q.defer(),
29520 result = resolution.promise,
29521 promises = result.$$promises = {},
29522 values = extend({}, locals),
29523 wait = 1 + plan.length/3,
29527 // Merge parent values we haven't got yet and publish our own $$values
29529 if (!merged) merge(values, parent.$$values);
29530 result.$$values = values;
29531 result.$$promises = result.$$promises || true; // keep for isResolve()
29532 delete result.$$inheritedValues;
29533 resolution.resolve(values);
29537 function fail(reason) {
29538 result.$$failure = reason;
29539 resolution.reject(reason);
29542 // Short-circuit if parent has already failed
29543 if (isDefined(parent.$$failure)) {
29544 fail(parent.$$failure);
29548 if (parent.$$inheritedValues) {
29549 merge(values, omit(parent.$$inheritedValues, invocableKeys));
29552 // Merge parent values if the parent has already resolved, or merge
29553 // parent promises and wait if the parent resolve is still in progress.
29554 extend(promises, parent.$$promises);
29555 if (parent.$$values) {
29556 merged = merge(values, omit(parent.$$values, invocableKeys));
29557 result.$$inheritedValues = omit(parent.$$values, invocableKeys);
29560 if (parent.$$inheritedValues) {
29561 result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
29563 parent.then(done, fail);
29566 // Process each invocable in the plan, but ignore any where a local of the same name exists.
29567 for (var i=0, ii=plan.length; i<ii; i+=3) {
29568 if (locals.hasOwnProperty(plan[i])) done();
29569 else invoke(plan[i], plan[i+1], plan[i+2]);
29572 function invoke(key, invocable, params) {
29573 // Create a deferred for this invocation. Failures will propagate to the resolution as well.
29574 var invocation = $q.defer(), waitParams = 0;
29575 function onfailure(reason) {
29576 invocation.reject(reason);
29579 // Wait for any parameter that we have a promise for (either from parent or from this
29580 // resolve; in that case study() will have made sure it's ordered before us in the plan).
29581 forEach(params, function (dep) {
29582 if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
29584 promises[dep].then(function (result) {
29585 values[dep] = result;
29586 if (!(--waitParams)) proceed();
29590 if (!waitParams) proceed();
29591 function proceed() {
29592 if (isDefined(result.$$failure)) return;
29594 invocation.resolve($injector.invoke(invocable, self, values));
29595 invocation.promise.then(function (result) {
29596 values[key] = result;
29603 // Publish promise synchronously; invocations further down in the plan may depend on it.
29604 promises[key] = invocation.promise;
29613 * @name ui.router.util.$resolve#resolve
29614 * @methodOf ui.router.util.$resolve
29617 * Resolves a set of invocables. An invocable is a function to be invoked via
29618 * `$injector.invoke()`, and can have an arbitrary number of dependencies.
29619 * An invocable can either return a value directly,
29620 * or a `$q` promise. If a promise is returned it will be resolved and the
29621 * resulting value will be used instead. Dependencies of invocables are resolved
29622 * (in this order of precedence)
29624 * - from the specified `locals`
29625 * - from another invocable that is part of this `$resolve` call
29626 * - from an invocable that is inherited from a `parent` call to `$resolve`
29628 * - from any ancestor `$resolve` of that parent).
29630 * The return value of `$resolve` is a promise for an object that contains
29631 * (in this order of precedence)
29633 * - any `locals` (if specified)
29634 * - the resolved return values of all injectables
29635 * - any values inherited from a `parent` call to `$resolve` (if specified)
29637 * The promise will resolve after the `parent` promise (if any) and all promises
29638 * returned by injectables have been resolved. If any invocable
29639 * (or `$injector.invoke`) throws an exception, or if a promise returned by an
29640 * invocable is rejected, the `$resolve` promise is immediately rejected with the
29641 * same error. A rejection of a `parent` promise (if specified) will likewise be
29642 * propagated immediately. Once the `$resolve` promise has been rejected, no
29643 * further invocables will be called.
29646 * Cyclic dependencies between invocables are not permitted and will cause `$resolve`
29648 * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
29649 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29650 * to throw an error. As a special case, an injectable can depend on a parameter
29651 * with the same name as the injectable, which will be fulfilled from the `parent`
29652 * injectable of the same name. This allows inherited values to be decorated.
29653 * Note that in this case any other injectable in the same `$resolve` with the same
29654 * dependency would see the decorated value, not the inherited value.
29656 * Note that missing dependencies -- unlike cyclic dependencies -- will cause an
29657 * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
29660 * Invocables are invoked eagerly as soon as all dependencies are available.
29661 * This is true even for dependencies inherited from a `parent` call to `$resolve`.
29663 * As a special case, an invocable can be a string, in which case it is taken to
29664 * be a service name to be passed to `$injector.get()`. This is supported primarily
29665 * for backwards-compatibility with the `resolve` property of `$routeProvider`
29668 * @param {object} invocables functions to invoke or
29669 * `$injector` services to fetch.
29670 * @param {object} locals values to make available to the injectables
29671 * @param {object} parent a promise returned by another call to `$resolve`.
29672 * @param {object} self the `this` for the invoked methods
29673 * @return {object} Promise for an object that contains the resolved return value
29674 * of all invocables, as well as any inherited and local values.
29676 this.resolve = function (invocables, locals, parent, self) {
29677 return this.study(invocables)(locals, parent, self);
29681 angular.module('ui.router.util').service('$resolve', $Resolve);
29686 * @name ui.router.util.$templateFactory
29689 * @requires $templateCache
29690 * @requires $injector
29693 * Service. Manages loading of templates.
29695 $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
29696 function $TemplateFactory( $http, $templateCache, $injector) {
29700 * @name ui.router.util.$templateFactory#fromConfig
29701 * @methodOf ui.router.util.$templateFactory
29704 * Creates a template from a configuration object.
29706 * @param {object} config Configuration object for which to load a template.
29707 * The following properties are search in the specified order, and the first one
29708 * that is defined is used to create the template:
29710 * @param {string|object} config.template html string template or function to
29711 * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29712 * @param {string|object} config.templateUrl url to load or a function returning
29713 * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
29714 * @param {Function} config.templateProvider function to invoke via
29715 * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
29716 * @param {object} params Parameters to pass to the template function.
29717 * @param {object} locals Locals to pass to `invoke` if the template is loaded
29718 * via a `templateProvider`. Defaults to `{ params: params }`.
29720 * @return {string|object} The template html as a string, or a promise for
29721 * that string,or `null` if no template is configured.
29723 this.fromConfig = function (config, params, locals) {
29725 isDefined(config.template) ? this.fromString(config.template, params) :
29726 isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
29727 isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
29734 * @name ui.router.util.$templateFactory#fromString
29735 * @methodOf ui.router.util.$templateFactory
29738 * Creates a template from a string or a function returning a string.
29740 * @param {string|object} template html template as a string or function that
29741 * returns an html template as a string.
29742 * @param {object} params Parameters to pass to the template function.
29744 * @return {string|object} The template html as a string, or a promise for that
29747 this.fromString = function (template, params) {
29748 return isFunction(template) ? template(params) : template;
29753 * @name ui.router.util.$templateFactory#fromUrl
29754 * @methodOf ui.router.util.$templateFactory
29757 * Loads a template from the a URL via `$http` and `$templateCache`.
29759 * @param {string|Function} url url of the template to load, or a function
29760 * that returns a url.
29761 * @param {Object} params Parameters to pass to the url function.
29762 * @return {string|Promise.<string>} The template html as a string, or a promise
29765 this.fromUrl = function (url, params) {
29766 if (isFunction(url)) url = url(params);
29767 if (url == null) return null;
29769 .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
29770 .then(function(response) { return response.data; });
29775 * @name ui.router.util.$templateFactory#fromProvider
29776 * @methodOf ui.router.util.$templateFactory
29779 * Creates a template by invoking an injectable provider function.
29781 * @param {Function} provider Function to invoke via `$injector.invoke`
29782 * @param {Object} params Parameters for the template.
29783 * @param {Object} locals Locals to pass to `invoke`. Defaults to
29784 * `{ params: params }`.
29785 * @return {string|Promise.<string>} The template html as a string, or a promise
29788 this.fromProvider = function (provider, params, locals) {
29789 return $injector.invoke(provider, null, locals || { params: params });
29793 angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
29795 var $$UMFP; // reference to $UrlMatcherFactoryProvider
29799 * @name ui.router.util.type:UrlMatcher
29802 * Matches URLs against patterns and extracts named parameters from the path or the search
29803 * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
29804 * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
29805 * do not influence whether or not a URL is matched, but their values are passed through into
29806 * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
29808 * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
29809 * syntax, which optionally allows a regular expression for the parameter to be specified:
29811 * * `':'` name - colon placeholder
29812 * * `'*'` name - catch-all placeholder
29813 * * `'{' name '}'` - curly placeholder
29814 * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
29815 * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
29817 * Parameter names may contain only word characters (latin letters, digits, and underscore) and
29818 * must be unique within the pattern (across both path and search parameters). For colon
29819 * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
29820 * number of characters other than '/'. For catch-all placeholders the path parameter matches
29821 * any number of characters.
29825 * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
29826 * trailing slashes, and patterns have to match the entire path, not just a prefix.
29827 * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
29828 * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
29829 * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
29830 * * `'/user/{id:[^/]*}'` - Same as the previous example.
29831 * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
29832 * parameter consists of 1 to 8 hex digits.
29833 * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
29834 * path into the parameter 'path'.
29835 * * `'/files/*path'` - ditto.
29836 * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
29837 * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
29839 * @param {string} pattern The pattern to compile into a matcher.
29840 * @param {Object} config A configuration object hash:
29841 * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
29842 * an existing UrlMatcher
29844 * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
29845 * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`.
29847 * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
29848 * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
29849 * non-null) will start with this prefix.
29851 * @property {string} source The pattern that was passed into the constructor
29853 * @property {string} sourcePath The path portion of the source property
29855 * @property {string} sourceSearch The search portion of the source property
29857 * @property {string} regex The constructed regex that will be used to match against the url when
29858 * it is time to determine which url will match.
29860 * @returns {Object} New `UrlMatcher` object
29862 function UrlMatcher(pattern, config, parentMatcher) {
29863 config = extend({ params: {} }, isObject(config) ? config : {});
29865 // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
29869 // '{' name ':' regexp '}'
29870 // The regular expression is somewhat complicated due to the need to allow curly braces
29871 // inside the regular expression. The placeholder regexp breaks down as follows:
29872 // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
29874 // \{([\w\[\]]+)(?:\:\s*( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29876 // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29877 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29878 // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
29879 // [^{}\\]+ - anything other than curly braces or backslash
29880 // \\. - a backslash escape
29881 // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
29883 var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29884 searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29886 var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29887 searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29888 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29889 compiled = '^', last = 0, m,
29890 segments = this.segments = [],
29891 parentParams = parentMatcher ? parentMatcher.params : {},
29892 params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
29895 function addParameter(id, type, config, location) {
29896 paramNames.push(id);
29897 if (parentParams[id]) return parentParams[id];
29899 if (!/^\w+([-.]+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29901 if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29902 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29903 if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
29904 params[id] = new $$UMFP.Param(id, type, config, location);
29908 function quoteRegExp(string, pattern, squash, optional) {
29909 var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
29910 if (!pattern) return result;
29912 case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
29915 result = result.replace(/\/$/, '');
29916 surroundPattern = ['(?:\/(', ')|\/)?'];
29919 case true: surroundPattern = ['?(', ')?']; break;
29920 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29921 default: surroundPattern = ['(' + squash + "|", ')?']; break;
29923 return result + surroundPattern[0] + pattern + surroundPattern[1];
29926 this.source = pattern;
29928 // Split into static segments separated by path parameter placeholders.
29929 // The number of segments is always 1 more than the number of parameters.
29930 function matchDetails(m, isSearch) {
29931 var id, regexp, segment, type, cfg, arrayMode;
29932 id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
29933 cfg = config.params[id];
29934 segment = pattern.substring(last, m.index);
29935 regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
29939 type = $$UMFP.type(regexp) || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29943 type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29944 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
29946 id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
29950 var p, param, segment;
29951 while ((m = placeholder.exec(pattern))) {
29952 p = matchDetails(m, false);
29953 if (p.segment.indexOf('?') >= 0) break; // we're into the search part
29955 param = addParameter(p.id, p.type, p.cfg, "path");
29956 compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
29957 segments.push(p.segment);
29958 last = placeholder.lastIndex;
29960 segment = pattern.substring(last);
29962 // Find any search parameter names and remove them from the last segment
29963 var i = segment.indexOf('?');
29966 var search = this.sourceSearch = segment.substring(i);
29967 segment = segment.substring(0, i);
29968 this.sourcePath = pattern.substring(0, last + i);
29970 if (search.length > 0) {
29972 while ((m = searchPlaceholder.exec(search))) {
29973 p = matchDetails(m, true);
29974 param = addParameter(p.id, p.type, p.cfg, "search");
29975 last = placeholder.lastIndex;
29980 this.sourcePath = pattern;
29981 this.sourceSearch = '';
29984 compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
29985 segments.push(segment);
29987 this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
29988 this.prefix = segments[0];
29989 this.$$paramNames = paramNames;
29994 * @name ui.router.util.type:UrlMatcher#concat
29995 * @methodOf ui.router.util.type:UrlMatcher
29998 * Returns a new matcher for a pattern constructed by appending the path part and adding the
29999 * search parameters of the specified pattern to this pattern. The current pattern is not
30000 * modified. This can be understood as creating a pattern for URLs that are relative to (or
30001 * suffixes of) the current pattern.
30004 * The following two matchers are equivalent:
30006 * new UrlMatcher('/user/{id}?q').concat('/details?date');
30007 * new UrlMatcher('/user/{id}/details?q&date');
30010 * @param {string} pattern The pattern to append.
30011 * @param {Object} config An object hash of the configuration for the matcher.
30012 * @returns {UrlMatcher} A matcher for the concatenated pattern.
30014 UrlMatcher.prototype.concat = function (pattern, config) {
30015 // Because order of search parameters is irrelevant, we can add our own search
30016 // parameters to the end of the new pattern. Parse the new pattern by itself
30017 // and then join the bits together, but it's much easier to do this on a string level.
30018 var defaultConfig = {
30019 caseInsensitive: $$UMFP.caseInsensitive(),
30020 strict: $$UMFP.strictMode(),
30021 squash: $$UMFP.defaultSquashPolicy()
30023 return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
30026 UrlMatcher.prototype.toString = function () {
30027 return this.source;
30032 * @name ui.router.util.type:UrlMatcher#exec
30033 * @methodOf ui.router.util.type:UrlMatcher
30036 * Tests the specified path against this matcher, and returns an object containing the captured
30037 * parameter values, or null if the path does not match. The returned object contains the values
30038 * of any search parameters that are mentioned in the pattern, but their value may be null if
30039 * they are not present in `searchParams`. This means that search parameters are always treated
30044 * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
30045 * x: '1', q: 'hello'
30047 * // returns { id: 'bob', q: 'hello', r: null }
30050 * @param {string} path The URL path to match, e.g. `$location.path()`.
30051 * @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
30052 * @returns {Object} The captured parameter values.
30054 UrlMatcher.prototype.exec = function (path, searchParams) {
30055 var m = this.regexp.exec(path);
30056 if (!m) return null;
30057 searchParams = searchParams || {};
30059 var paramNames = this.parameters(), nTotal = paramNames.length,
30060 nPath = this.segments.length - 1,
30061 values = {}, i, j, cfg, paramName;
30063 if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
30065 function decodePathArray(string) {
30066 function reverseString(str) { return str.split("").reverse().join(""); }
30067 function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
30069 var split = reverseString(string).split(/-(?!\\)/);
30070 var allReversed = map(split, reverseString);
30071 return map(allReversed, unquoteDashes).reverse();
30075 var param, paramVal;
30076 for (i = 0; i < nPath; i++) {
30077 paramName = paramNames[i];
30078 param = this.params[paramName];
30080 // if the param value matches a pre-replace pair, replace the value before decoding.
30081 for (j = 0; j < param.replace.length; j++) {
30082 if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30084 if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30085 if (isDefined(paramVal)) paramVal = param.type.decode(paramVal);
30087 for (i = 0; i < nPath; i++) {
30088 paramName = paramNames[i];
30089 var param = this.params[paramName];
30090 var paramVal = m[i+1];
30091 // if the param value matches a pre-replace pair, replace the value before decoding.
30092 for (j = 0; j < param.replace; j++) {
30093 if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30095 if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30096 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30097 values[paramName] = param.value(paramVal);
30099 for (/**/; i < nTotal; i++) {
30100 paramName = paramNames[i];
30101 values[paramName] = this.params[paramName].value(searchParams[paramName]);
30103 param = this.params[paramName];
30104 paramVal = searchParams[paramName];
30105 for (j = 0; j < param.replace.length; j++) {
30106 if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30108 if (isDefined(paramVal)) paramVal = param.type.decode(paramVal);
30109 values[paramName] = param.value(paramVal);
30111 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30119 * @name ui.router.util.type:UrlMatcher#parameters
30120 * @methodOf ui.router.util.type:UrlMatcher
30123 * Returns the names of all path and search parameters of this pattern in an unspecified order.
30125 * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the
30126 * pattern has no parameters, an empty array is returned.
30128 UrlMatcher.prototype.parameters = function (param) {
30129 if (!isDefined(param)) return this.$$paramNames;
30130 return this.params[param] || null;
30136 * @name ui.router.util.type:UrlMatcher#validates
30138 * @name ui.router.util.type:UrlMatcher#validate
30139 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30140 * @methodOf ui.router.util.type:UrlMatcher
30143 * Checks an object hash of parameters to validate their correctness according to the parameter
30144 * types of this `UrlMatcher`.
30146 * @param {Object} params The object hash of parameters to validate.
30147 * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
30149 UrlMatcher.prototype.validates = function (params) {
30150 return this.params.$$validates(params);
30155 * @name ui.router.util.type:UrlMatcher#format
30156 * @methodOf ui.router.util.type:UrlMatcher
30159 * Creates a URL that matches this pattern by substituting the specified values
30160 * for the path and search parameters. Null values for path parameters are
30161 * treated as empty strings.
30165 * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
30166 * // returns '/user/bob?q=yes'
30169 * @param {Object} values the values to substitute for the parameters in this pattern.
30170 * @returns {string} the formatted URL (path and optionally search part).
30172 UrlMatcher.prototype.format = function (values) {
30173 values = values || {};
30174 var segments = this.segments, params = this.parameters(), paramset = this.params;
30175 if (!this.validates(values)) return null;
30177 var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
30179 function encodeDashes(str) { // Replace dashes with encoded "\-"
30180 return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
30183 for (i = 0; i < nTotal; i++) {
30184 var isPathParam = i < nPath;
30185 var name = params[i], param = paramset[name], value = param.value(values[name]);
30186 var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
30187 var squash = isDefaultValue ? param.squash : false;
30188 var encoded = param.type.encode(value);
30191 var nextSegment = segments[i + 1];
30193 var isFinalPathParam = i + 1 === nPath;
30196 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30197 if (squash === false) {
30198 if (encoded != null) {
30199 if (isArray(encoded)) {
30200 result += map(encoded, encodeDashes).join("-");
30202 result += encodeURIComponent(encoded);
30205 result += nextSegment;
30206 } else if (squash === true) {
30207 var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
30208 result += nextSegment.match(capture)[1];
30209 } else if (isString(squash)) {
30210 result += squash + nextSegment;
30214 if (isFinalPathParam && param.squash === true && result.slice(-1) === '/') result = result.slice(0, -1);
30216 if (encoded == null || (isDefaultValue && squash !== false)) continue;
30217 if (!isArray(encoded)) encoded = [ encoded ];
30218 if (encoded.length === 0) continue;
30221 if (encoded == null || (isDefaultValue && squash !== false)) continue;
30222 if (!isArray(encoded)) encoded = [ encoded ];
30223 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30224 encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
30225 result += (search ? '&' : '?') + (name + '=' + encoded);
30235 * @name ui.router.util.type:Type
30238 * Implements an interface to define custom parameter types that can be decoded from and encoded to
30239 * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
30240 * objects when matching or formatting URLs, or comparing or validating parameter values.
30242 * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
30243 * information on registering custom types.
30245 * @param {Object} config A configuration object which contains the custom type definition. The object's
30246 * properties will override the default methods and/or pattern in `Type`'s public interface.
30250 * decode: function(val) { return parseInt(val, 10); },
30251 * encode: function(val) { return val && val.toString(); },
30252 * equals: function(a, b) { return this.is(a) && a === b; },
30253 * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
30258 * @property {RegExp} pattern The regular expression pattern used to match values of this type when
30259 * coming from a substring of a URL.
30261 * @returns {Object} Returns a new `Type` object.
30263 function Type(config) {
30264 extend(this, config);
30269 * @name ui.router.util.type:Type#is
30270 * @methodOf ui.router.util.type:Type
30273 * Detects whether a value is of a particular type. Accepts a native (decoded) value
30274 * and determines whether it matches the current `Type` object.
30276 * @param {*} val The value to check.
30277 * @param {string} key Optional. If the type check is happening in the context of a specific
30278 * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
30279 * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
30280 * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`.
30282 Type.prototype.is = function(val, key) {
30288 * @name ui.router.util.type:Type#encode
30289 * @methodOf ui.router.util.type:Type
30292 * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
30293 * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
30294 * only needs to be a representation of `val` that has been coerced to a string.
30296 * @param {*} val The value to encode.
30297 * @param {string} key The name of the parameter in which `val` is stored. Can be used for
30298 * meta-programming of `Type` objects.
30299 * @returns {string} Returns a string representation of `val` that can be encoded in a URL.
30301 Type.prototype.encode = function(val, key) {
30307 * @name ui.router.util.type:Type#decode
30308 * @methodOf ui.router.util.type:Type
30311 * Converts a parameter value (from URL string or transition param) to a custom/native value.
30313 * @param {string} val The URL parameter value to decode.
30314 * @param {string} key The name of the parameter in which `val` is stored. Can be used for
30315 * meta-programming of `Type` objects.
30316 * @returns {*} Returns a custom representation of the URL parameter value.
30318 Type.prototype.decode = function(val, key) {
30324 * @name ui.router.util.type:Type#equals
30325 * @methodOf ui.router.util.type:Type
30328 * Determines whether two decoded values are equivalent.
30330 * @param {*} a A value to compare against.
30331 * @param {*} b A value to compare against.
30332 * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`.
30334 Type.prototype.equals = function(a, b) {
30338 Type.prototype.$subPattern = function() {
30339 var sub = this.pattern.toString();
30340 return sub.substr(1, sub.length - 2);
30343 Type.prototype.pattern = /.*/;
30345 Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
30347 /** Given an encoded string, or a decoded object, returns a decoded object */
30348 Type.prototype.$normalize = function(val) {
30349 return this.is(val) ? val : this.decode(val);
30353 * Wraps an existing custom Type as an array of Type, depending on 'mode'.
30355 * - urlmatcher pattern "/path?{queryParam[]:int}"
30356 * - url: "/path?queryParam=1&queryParam=2
30357 * - $stateParams.queryParam will be [1, 2]
30358 * if `mode` is "auto", then
30359 * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
30360 * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
30362 Type.prototype.$asArray = function(mode, isSearch) {
30363 if (!mode) return this;
30364 if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
30366 function ArrayType(type, mode) {
30367 function bindTo(type, callbackName) {
30368 return function() {
30369 return type[callbackName].apply(type, arguments);
30373 // Wrap non-array value as array
30374 function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
30375 // Unwrap array value for "auto" mode. Return undefined for empty array.
30376 function arrayUnwrap(val) {
30377 switch(val.length) {
30378 case 0: return undefined;
30379 case 1: return mode === "auto" ? val[0] : val;
30380 default: return val;
30383 function falsey(val) { return !val; }
30385 // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
30386 function arrayHandler(callback, allTruthyMode) {
30387 return function handleArray(val) {
30389 if (isArray(val) && val.length === 0) return val;
30391 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30392 val = arrayWrap(val);
30393 var result = map(val, callback);
30394 if (allTruthyMode === true)
30395 return filter(result, falsey).length === 0;
30396 return arrayUnwrap(result);
30400 // Wraps type (.equals) functions to operate on each value of an array
30401 function arrayEqualsHandler(callback) {
30402 return function handleArray(val1, val2) {
30403 var left = arrayWrap(val1), right = arrayWrap(val2);
30404 if (left.length !== right.length) return false;
30405 for (var i = 0; i < left.length; i++) {
30406 if (!callback(left[i], right[i])) return false;
30412 this.encode = arrayHandler(bindTo(type, 'encode'));
30413 this.decode = arrayHandler(bindTo(type, 'decode'));
30414 this.is = arrayHandler(bindTo(type, 'is'), true);
30415 this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
30416 this.pattern = type.pattern;
30417 this.$normalize = arrayHandler(bindTo(type, '$normalize'));
30418 this.name = type.name;
30419 this.$arrayMode = mode;
30422 return new ArrayType(this, mode);
30429 * @name ui.router.util.$urlMatcherFactory
30432 * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
30433 * is also available to providers under the name `$urlMatcherFactoryProvider`.
30435 function $UrlMatcherFactory() {
30438 var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
30441 // Use tildes to pre-encode slashes.
30442 // If the slashes are simply URLEncoded, the browser can choose to pre-decode them,
30443 // and bidirectional encoding/decoding fails.
30444 // Tilde was chosen because it's not a RFC 3986 section 2.2 Reserved Character
30445 function valToString(val) { return val != null ? val.toString().replace(/~/g, "~~").replace(/\//g, "~2F") : val; }
30446 function valFromString(val) { return val != null ? val.toString().replace(/~2F/g, "/").replace(/~~/g, "~") : val; }
30448 var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30451 function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
30452 function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
30454 var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30456 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30457 encode: valToString,
30458 decode: valFromString,
30459 // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
30460 // In 0.2.x, string params are optional by default for backwards compat
30461 is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
30468 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30469 encode: valToString,
30470 decode: function(val) { return parseInt(val, 10); },
30471 is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
30478 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30479 encode: function(val) { return val ? 1 : 0; },
30480 decode: function(val) { return parseInt(val, 10) !== 0; },
30481 is: function(val) { return val === true || val === false; },
30488 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30489 encode: function (val) {
30492 return [ val.getFullYear(),
30493 ('0' + (val.getMonth() + 1)).slice(-2),
30494 ('0' + val.getDate()).slice(-2)
30497 decode: function (val) {
30498 if (this.is(val)) return val;
30499 var match = this.capture.exec(val);
30500 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
30502 is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
30503 equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
30504 pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
30505 capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
30511 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30512 encode: angular.toJson,
30513 decode: angular.fromJson,
30514 is: angular.isObject,
30515 equals: angular.equals,
30519 "any": { // does not encode/decode
30521 any: { // does not encode/decode
30522 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30523 encode: angular.identity,
30524 decode: angular.identity,
30525 equals: angular.equals,
30530 function getDefaultConfig() {
30532 strict: isStrictMode,
30533 caseInsensitive: isCaseInsensitive
30537 function isInjectable(value) {
30538 return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
30542 * [Internal] Get the default value of a parameter, which may be an injectable function.
30544 $UrlMatcherFactory.$$getDefaultValue = function(config) {
30545 if (!isInjectable(config.value)) return config.value;
30546 if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30547 return injector.invoke(config.value);
30552 * @name ui.router.util.$urlMatcherFactory#caseInsensitive
30553 * @methodOf ui.router.util.$urlMatcherFactory
30556 * Defines whether URL matching should be case sensitive (the default behavior), or not.
30558 * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
30559 * @returns {boolean} the current value of caseInsensitive
30561 this.caseInsensitive = function(value) {
30562 if (isDefined(value))
30563 isCaseInsensitive = value;
30564 return isCaseInsensitive;
30569 * @name ui.router.util.$urlMatcherFactory#strictMode
30570 * @methodOf ui.router.util.$urlMatcherFactory
30573 * Defines whether URLs should match trailing slashes, or not (the default behavior).
30575 * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
30576 * @returns {boolean} the current value of strictMode
30578 this.strictMode = function(value) {
30579 if (isDefined(value))
30580 isStrictMode = value;
30581 return isStrictMode;
30586 * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
30587 * @methodOf ui.router.util.$urlMatcherFactory
30590 * Sets the default behavior when generating or matching URLs with default parameter values.
30592 * @param {string} value A string that defines the default parameter URL squashing behavior.
30593 * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
30594 * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
30595 * parameter is surrounded by slashes, squash (remove) one slash from the URL
30596 * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
30597 * the parameter value from the URL and replace it with this string.
30599 this.defaultSquashPolicy = function(value) {
30600 if (!isDefined(value)) return defaultSquashPolicy;
30601 if (value !== true && value !== false && !isString(value))
30602 throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
30603 defaultSquashPolicy = value;
30609 * @name ui.router.util.$urlMatcherFactory#compile
30610 * @methodOf ui.router.util.$urlMatcherFactory
30613 * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
30615 * @param {string} pattern The URL pattern.
30616 * @param {Object} config The config object hash.
30617 * @returns {UrlMatcher} The UrlMatcher.
30619 this.compile = function (pattern, config) {
30620 return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
30625 * @name ui.router.util.$urlMatcherFactory#isMatcher
30626 * @methodOf ui.router.util.$urlMatcherFactory
30629 * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
30631 * @param {Object} object The object to perform the type check against.
30632 * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by
30633 * implementing all the same methods.
30635 this.isMatcher = function (o) {
30636 if (!isObject(o)) return false;
30639 forEach(UrlMatcher.prototype, function(val, name) {
30640 if (isFunction(val)) {
30641 result = result && (isDefined(o[name]) && isFunction(o[name]));
30649 * @name ui.router.util.$urlMatcherFactory#type
30650 * @methodOf ui.router.util.$urlMatcherFactory
30653 * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
30654 * generate URLs with typed parameters.
30656 * @param {string} name The type name.
30657 * @param {Object|Function} definition The type definition. See
30658 * {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30659 * @param {Object|Function} definitionFn (optional) A function that is injected before the app
30660 * runtime starts. The result of this function is merged into the existing `definition`.
30661 * See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30663 * @returns {Object} Returns `$urlMatcherFactoryProvider`.
30666 * This is a simple example of a custom type that encodes and decodes items from an
30667 * array, using the array index as the URL-encoded value:
30670 * var list = ['John', 'Paul', 'George', 'Ringo'];
30672 * $urlMatcherFactoryProvider.type('listItem', {
30673 * encode: function(item) {
30674 * // Represent the list item in the URL using its corresponding index
30675 * return list.indexOf(item);
30677 * decode: function(item) {
30678 * // Look up the list item by index
30679 * return list[parseInt(item, 10)];
30681 * is: function(item) {
30682 * // Ensure the item is valid by checking to see that it appears
30684 * return list.indexOf(item) > -1;
30688 * $stateProvider.state('list', {
30689 * url: "/list/{item:listItem}",
30690 * controller: function($scope, $stateParams) {
30691 * console.log($stateParams.item);
30697 * // Changes URL to '/list/3', logs "Ringo" to the console
30698 * $state.go('list', { item: "Ringo" });
30701 * This is a more complex example of a type that relies on dependency injection to
30702 * interact with services, and uses the parameter name from the URL to infer how to
30703 * handle encoding and decoding parameter values:
30706 * // Defines a custom type that gets a value from a service,
30707 * // where each service gets different types of values from
30708 * // a backend API:
30709 * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
30711 * // Matches up services to URL parameter names
30718 * encode: function(object) {
30719 * // Represent the object in the URL using its unique ID
30720 * return object.id;
30722 * decode: function(value, key) {
30723 * // Look up the object by ID, using the parameter
30724 * // name (key) to call the correct service
30725 * return services[key].findById(value);
30727 * is: function(object, key) {
30728 * // Check that object is a valid dbObject
30729 * return angular.isObject(object) && object.id && services[key];
30731 * equals: function(a, b) {
30732 * // Check the equality of decoded objects by comparing
30733 * // their unique IDs
30734 * return a.id === b.id;
30739 * // In a config() block, you can then attach URLs with
30740 * // type-annotated parameters:
30741 * $stateProvider.state('users', {
30744 * }).state('users.item', {
30745 * url: "/{user:dbObject}",
30746 * controller: function($scope, $stateParams) {
30747 * // $stateParams.user will now be an object returned from
30748 * // the Users service
30754 this.type = function (name, definition, definitionFn) {
30755 if (!isDefined(definition)) return $types[name];
30756 if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
30758 $types[name] = new Type(extend({ name: name }, definition));
30759 if (definitionFn) {
30760 typeQueue.push({ name: name, def: definitionFn });
30761 if (!enqueue) flushTypeQueue();
30766 // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
30767 function flushTypeQueue() {
30768 while(typeQueue.length) {
30769 var type = typeQueue.shift();
30770 if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
30771 angular.extend($types[type.name], injector.invoke(type.def));
30775 // Register default types. Store them in the prototype of $types.
30776 forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
30777 $types = inherit($types, {});
30779 /* No need to document $get, since it returns this */
30780 this.$get = ['$injector', function ($injector) {
30781 injector = $injector;
30785 forEach(defaultTypes, function(type, name) {
30786 if (!$types[name]) $types[name] = new Type(type);
30791 this.Param = function Param(id, type, config, location) {
30793 config = unwrapShorthand(config);
30794 type = getType(config, type, location);
30795 var arrayMode = getArrayMode();
30796 type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
30797 if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
30798 config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
30799 var isOptional = config.value !== undefined;
30800 var squash = getSquashPolicy(config, isOptional);
30801 var replace = getReplace(config, arrayMode, isOptional, squash);
30803 function unwrapShorthand(config) {
30804 var keys = isObject(config) ? objectKeys(config) : [];
30805 var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
30806 indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
30807 if (isShorthand) config = { value: config };
30808 config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
30812 function getType(config, urlType, location) {
30813 if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
30814 if (urlType) return urlType;
30815 if (!config.type) return (location === "config" ? $types.any : $types.string);
30818 if (angular.isString(config.type))
30819 return $types[config.type];
30820 if (config.type instanceof Type)
30821 return config.type;
30822 return new Type(config.type);
30824 return config.type instanceof Type ? config.type : new Type(config.type);
30825 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
30828 // array config: param name (param[]) overrides default settings. explicit config overrides param name.
30829 function getArrayMode() {
30830 var arrayDefaults = { array: (location === "search" ? "auto" : false) };
30831 var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
30832 return extend(arrayDefaults, arrayParamNomenclature, config).array;
30836 * returns false, true, or the squash value to indicate the "default parameter url squash policy".
30838 function getSquashPolicy(config, isOptional) {
30839 var squash = config.squash;
30840 if (!isOptional || squash === false) return false;
30841 if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
30842 if (squash === true || isString(squash)) return squash;
30843 throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
30846 function getReplace(config, arrayMode, isOptional, squash) {
30847 var replace, configuredKeys, defaultPolicy = [
30848 { from: "", to: (isOptional || arrayMode ? undefined : "") },
30849 { from: null, to: (isOptional || arrayMode ? undefined : "") }
30851 replace = isArray(config.replace) ? config.replace : [];
30852 if (isString(squash))
30853 replace.push({ from: squash, to: undefined });
30854 configuredKeys = map(replace, function(item) { return item.from; } );
30855 return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
30859 * [Internal] Get the default value of a parameter, which may be an injectable function.
30861 function $$getDefaultValue() {
30862 if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30863 var defaultValue = injector.invoke(config.$$fn);
30864 if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
30865 throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
30866 return defaultValue;
30870 * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
30871 * default value, which may be the result of an injectable function.
30873 function $value(value) {
30874 function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
30875 function $replace(value) {
30876 var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
30877 return replacement.length ? replacement[0] : value;
30879 value = $replace(value);
30880 return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
30883 function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
30888 location: location,
30892 isOptional: isOptional,
30894 dynamic: undefined,
30900 function ParamSet(params) {
30901 extend(this, params || {});
30904 ParamSet.prototype = {
30905 $$new: function() {
30906 return inherit(this, extend(new ParamSet(), { $$parent: this}));
30908 $$keys: function () {
30909 var keys = [], chain = [], parent = this,
30910 ignore = objectKeys(ParamSet.prototype);
30911 while (parent) { chain.push(parent); parent = parent.$$parent; }
30913 forEach(chain, function(paramset) {
30914 forEach(objectKeys(paramset), function(key) {
30915 if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
30920 $$values: function(paramValues) {
30921 var values = {}, self = this;
30922 forEach(self.$$keys(), function(key) {
30923 values[key] = self[key].value(paramValues && paramValues[key]);
30927 $$equals: function(paramValues1, paramValues2) {
30928 var equal = true, self = this;
30929 forEach(self.$$keys(), function(key) {
30930 var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
30931 if (!self[key].type.equals(left, right)) equal = false;
30935 $$validates: function $$validate(paramValues) {
30936 var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
30937 for (i = 0; i < keys.length; i++) {
30938 param = this[keys[i]];
30939 rawVal = paramValues[keys[i]];
30940 if ((rawVal === undefined || rawVal === null) && param.isOptional)
30941 break; // There was no parameter value, but the param is optional
30942 normalized = param.type.$normalize(rawVal);
30943 if (!param.type.is(normalized))
30944 return false; // The value was not of the correct Type, and could not be decoded to the correct Type
30945 encoded = param.type.encode(normalized);
30946 if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
30947 return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
30951 $$parent: undefined
30954 this.ParamSet = ParamSet;
30957 // Register as a provider so it's available to other providers
30958 angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
30959 angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
30963 * @name ui.router.router.$urlRouterProvider
30965 * @requires ui.router.util.$urlMatcherFactoryProvider
30966 * @requires $locationProvider
30969 * `$urlRouterProvider` has the responsibility of watching `$location`.
30970 * When `$location` changes it runs through a list of rules one by one until a
30971 * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify
30972 * a url in a state configuration. All urls are compiled into a UrlMatcher object.
30974 * There are several methods on `$urlRouterProvider` that make it useful to use directly
30975 * in your module config.
30977 $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
30978 function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
30979 var rules = [], otherwise = null, interceptDeferred = false, listener;
30981 // Returns a string that is a prefix of all strings matching the RegExp
30982 function regExpPrefix(re) {
30983 var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
30984 return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
30987 // Interpolates matched values into a String.replace()-style pattern
30988 function interpolate(pattern, match) {
30989 return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30990 return match[what === '$' ? 0 : Number(what)];
30996 * @name ui.router.router.$urlRouterProvider#rule
30997 * @methodOf ui.router.router.$urlRouterProvider
31000 * Defines rules that are used by `$urlRouterProvider` to find matches for
31005 * var app = angular.module('app', ['ui.router.router']);
31007 * app.config(function ($urlRouterProvider) {
31008 * // Here's an example of how you might allow case insensitive urls
31009 * $urlRouterProvider.rule(function ($injector, $location) {
31010 * var path = $location.path(),
31011 * normalized = path.toLowerCase();
31013 * if (path !== normalized) {
31014 * return normalized;
31021 * @param {function} rule Handler function that takes `$injector` and `$location`
31023 * @param {object} rule Handler function that takes `$injector` and `$location`
31024 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31025 * services as arguments. You can use them to return a valid path as a string.
31027 * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
31029 this.rule = function (rule) {
31030 if (!isFunction(rule)) throw new Error("'rule' must be a function");
31037 * @name ui.router.router.$urlRouterProvider#otherwise
31038 * @methodOf ui.router.router.$urlRouterProvider
31041 * Defines a path that is used when an invalid route is requested.
31045 * var app = angular.module('app', ['ui.router.router']);
31047 * app.config(function ($urlRouterProvider) {
31048 * // if the path doesn't match any of the urls you configured
31049 * // otherwise will take care of routing the user to the
31051 * $urlRouterProvider.otherwise('/index');
31053 * // Example of using function rule as param
31054 * $urlRouterProvider.otherwise(function ($injector, $location) {
31055 * return '/a/valid/url';
31061 * @param {string|function} rule The url path you want to redirect to or a function
31063 * @param {string|object} rule The url path you want to redirect to or a function
31064 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31065 * rule that returns the url path. The function version is passed two params:
31066 * `$injector` and `$location` services, and must return a url string.
31068 * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
31070 this.otherwise = function (rule) {
31071 if (isString(rule)) {
31072 var redirect = rule;
31073 rule = function () { return redirect; };
31075 else if (!isFunction(rule)) throw new Error("'rule' must be a function");
31081 function handleIfMatch($injector, handler, match) {
31082 if (!match) return false;
31083 var result = $injector.invoke(handler, handler, { $match: match });
31084 return isDefined(result) ? result : true;
31089 * @name ui.router.router.$urlRouterProvider#when
31090 * @methodOf ui.router.router.$urlRouterProvider
31094 * Registers a handler for a given url matching.
31096 * If the handler is a string, it is
31098 * Registers a handler for a given url matching. if handle is a string, it is
31099 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31100 * treated as a redirect, and is interpolated according to the syntax of match
31101 * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
31103 * If the handler is a function, it is injectable. It gets invoked if `$location`
31104 * matches. You have the option of inject the match object as `$match`.
31106 * The handler can return
31108 * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
31109 * will continue trying to find another one that matches.
31110 * - **string** which is treated as a redirect and passed to `$location.url()`
31111 * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
31115 * var app = angular.module('app', ['ui.router.router']);
31117 * app.config(function ($urlRouterProvider) {
31118 * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
31119 * if ($state.$current.navigable !== state ||
31120 * !equalForKeys($match, $stateParams) {
31121 * $state.transitionTo(state, $match, false);
31127 * @param {string|object} what The incoming path that you want to redirect.
31129 * @param {string|function} handler The path you want to redirect your user to.
31131 * @param {string|object} handler The path you want to redirect your user to.
31132 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31134 this.when = function (what, handler) {
31135 var redirect, handlerIsString = isString(handler);
31136 if (isString(what)) what = $urlMatcherFactory.compile(what);
31138 if (!handlerIsString && !isFunction(handler) && !isArray(handler))
31139 throw new Error("invalid 'handler' in when()");
31142 matcher: function (what, handler) {
31143 if (handlerIsString) {
31144 redirect = $urlMatcherFactory.compile(handler);
31145 handler = ['$match', function ($match) { return redirect.format($match); }];
31147 return extend(function ($injector, $location) {
31148 return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
31150 prefix: isString(what.prefix) ? what.prefix : ''
31153 regex: function (what, handler) {
31154 if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
31156 if (handlerIsString) {
31157 redirect = handler;
31158 handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
31160 return extend(function ($injector, $location) {
31161 return handleIfMatch($injector, handler, what.exec($location.path()));
31163 prefix: regExpPrefix(what)
31168 var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
31170 for (var n in check) {
31171 if (check[n]) return this.rule(strategies[n](what, handler));
31174 throw new Error("invalid 'what' in when()");
31179 * @name ui.router.router.$urlRouterProvider#deferIntercept
31180 * @methodOf ui.router.router.$urlRouterProvider
31183 * Disables (or enables) deferring location change interception.
31185 * If you wish to customize the behavior of syncing the URL (for example, if you wish to
31186 * defer a transition but maintain the current URL), call this method at configuration time.
31187 * Then, at run time, call `$urlRouter.listen()` after you have configured your own
31188 * `$locationChangeSuccess` event handler.
31192 * var app = angular.module('app', ['ui.router.router']);
31194 * app.config(function ($urlRouterProvider) {
31196 * // Prevent $urlRouter from automatically intercepting URL changes;
31197 * // this allows you to configure custom behavior in between
31198 * // location changes and route synchronization:
31199 * $urlRouterProvider.deferIntercept();
31201 * }).run(function ($rootScope, $urlRouter, UserService) {
31203 * $rootScope.$on('$locationChangeSuccess', function(e) {
31204 * // UserService is an example service for managing user state
31205 * if (UserService.isLoggedIn()) return;
31207 * // Prevent $urlRouter's default handler from firing
31208 * e.preventDefault();
31210 * UserService.handleLogin().then(function() {
31211 * // Once the user has logged in, sync the current URL
31212 * // to the router:
31213 * $urlRouter.sync();
31217 * // Configures $urlRouter's listener *after* your custom listener
31218 * $urlRouter.listen();
31222 * @param {boolean} defer Indicates whether to defer location change interception. Passing
31223 no parameter is equivalent to `true`.
31225 this.deferIntercept = function (defer) {
31226 if (defer === undefined) defer = true;
31227 interceptDeferred = defer;
31232 * @name ui.router.router.$urlRouter
31234 * @requires $location
31235 * @requires $rootScope
31236 * @requires $injector
31237 * @requires $browser
31244 $get.$inject = ['$location', '$rootScope', '$injector', '$browser', '$sniffer'];
31245 function $get( $location, $rootScope, $injector, $browser, $sniffer) {
31247 $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
31248 function $get( $location, $rootScope, $injector, $browser) {
31249 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31251 var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
31253 function appendBasePath(url, isHtml5, absolute) {
31254 if (baseHref === '/') return url;
31255 if (isHtml5) return baseHref.slice(0, -1) + url;
31256 if (absolute) return baseHref.slice(1) + url;
31260 // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
31261 function update(evt) {
31262 if (evt && evt.defaultPrevented) return;
31263 var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
31264 lastPushedUrl = undefined;
31265 // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
31266 //if (ignoreUpdate) return true;
31268 function check(rule) {
31269 var handled = rule($injector, $location);
31271 if (!handled) return false;
31272 if (isString(handled)) $location.replace().url(handled);
31275 var n = rules.length, i;
31277 for (i = 0; i < n; i++) {
31278 if (check(rules[i])) return;
31280 // always check otherwise last to allow dynamic updates to the set of rules
31281 if (otherwise) check(otherwise);
31284 function listen() {
31285 listener = listener || $rootScope.$on('$locationChangeSuccess', update);
31289 if (!interceptDeferred) listen();
31294 * @name ui.router.router.$urlRouter#sync
31295 * @methodOf ui.router.router.$urlRouter
31298 * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
31299 * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
31300 * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
31301 * with the transition by calling `$urlRouter.sync()`.
31305 * angular.module('app', ['ui.router'])
31306 * .run(function($rootScope, $urlRouter) {
31307 * $rootScope.$on('$locationChangeSuccess', function(evt) {
31308 * // Halt state change from even starting
31309 * evt.preventDefault();
31310 * // Perform custom logic
31311 * var meetsRequirement = ...
31312 * // Continue with the update and state transition if logic allows
31313 * if (meetsRequirement) $urlRouter.sync();
31322 listen: function() {
31326 update: function(read) {
31328 location = $location.url();
31331 if ($location.url() === location) return;
31333 $location.url(location);
31334 $location.replace();
31337 push: function(urlMatcher, params, options) {
31338 var url = urlMatcher.format(params || {});
31340 // Handle the special hash param, if needed
31341 if (url !== null && params && params['#']) {
31342 url += '#' + params['#'];
31345 $location.url(url);
31346 lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
31347 if (options && options.replace) $location.replace();
31352 * @name ui.router.router.$urlRouter#href
31353 * @methodOf ui.router.router.$urlRouter
31356 * A URL generation method that returns the compiled URL for a given
31357 * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
31361 * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
31364 * // $bob == "/about/bob";
31367 * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
31368 * @param {object=} params An object of parameter values to fill the matcher's required parameters.
31369 * @param {object=} options Options object. The options are:
31371 * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
31373 * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
31375 href: function(urlMatcher, params, options) {
31376 if (!urlMatcher.validates(params)) return null;
31378 var isHtml5 = $locationProvider.html5Mode();
31379 if (angular.isObject(isHtml5)) {
31380 isHtml5 = isHtml5.enabled;
31384 isHtml5 = isHtml5 && $sniffer.history;
31386 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31388 var url = urlMatcher.format(params);
31389 options = options || {};
31391 if (!isHtml5 && url !== null) {
31392 url = "#" + $locationProvider.hashPrefix() + url;
31395 // Handle special hash param, if needed
31396 if (url !== null && params && params['#']) {
31397 url += '#' + params['#'];
31400 url = appendBasePath(url, isHtml5, options.absolute);
31402 if (!options.absolute || !url) {
31406 var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
31407 port = (port === 80 || port === 443 ? '' : ':' + port);
31409 return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
31415 angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
31419 * @name ui.router.state.$stateProvider
31421 * @requires ui.router.router.$urlRouterProvider
31422 * @requires ui.router.util.$urlMatcherFactoryProvider
31425 * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
31428 * A state corresponds to a "place" in the application in terms of the overall UI and
31429 * navigation. A state describes (via the controller / template / view properties) what
31430 * the UI looks like and does at that place.
31432 * States often have things in common, and the primary way of factoring out these
31433 * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
31436 * The `$stateProvider` provides interfaces to declare these states for your app.
31438 $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
31439 function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
31441 var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
31443 // Builds state properties from definition passed to registerState()
31444 var stateBuilder = {
31446 // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31447 // state.children = [];
31448 // if (parent) parent.children.push(state);
31449 parent: function(state) {
31450 if (isDefined(state.parent) && state.parent) return findState(state.parent);
31451 // regex matches any valid composite state name
31452 // would match "contact.list" but not "contacts"
31453 var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
31454 return compositeName ? findState(compositeName[1]) : root;
31457 // inherit 'data' from parent and override by own values (if any)
31458 data: function(state) {
31459 if (state.parent && state.parent.data) {
31461 state.data = state.self.data = inherit(state.parent.data, state.data);
31463 state.data = state.self.data = extend({}, state.parent.data, state.data);
31464 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31469 // Build a URLMatcher if necessary, either via a relative or absolute URL
31470 url: function(state) {
31471 var url = state.url, config = { params: state.params || {} };
31473 if (isString(url)) {
31474 if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
31475 return (state.parent.navigable || root).url.concat(url, config);
31478 if (!url || $urlMatcherFactory.isMatcher(url)) return url;
31479 throw new Error("Invalid url '" + url + "' in state '" + state + "'");
31482 // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
31483 navigable: function(state) {
31484 return state.url ? state : (state.parent ? state.parent.navigable : null);
31487 // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
31488 ownParams: function(state) {
31489 var params = state.url && state.url.params || new $$UMFP.ParamSet();
31490 forEach(state.params || {}, function(config, id) {
31491 if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
31496 // Derive parameters for this state and ensure they're a super-set of parent's parameters
31497 params: function(state) {
31499 var ownParams = pick(state.ownParams, state.ownParams.$$keys());
31500 return state.parent && state.parent.params ? extend(state.parent.params.$$new(), ownParams) : new $$UMFP.ParamSet();
31502 return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
31503 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31506 // If there is no explicit multi-view configuration, make one up so we don't have
31507 // to handle both cases in the view directive later. Note that having an explicit
31508 // 'views' property will mean the default unnamed view properties are ignored. This
31509 // is also a good time to resolve view names to absolute names, so everything is a
31510 // straight lookup at link time.
31511 views: function(state) {
31514 forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
31515 if (name.indexOf('@') < 0) name += '@' + state.parent.name;
31516 views[name] = view;
31521 // Keep a full path from the root down to this state as this is needed for state activation.
31522 path: function(state) {
31523 return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
31526 // Speed up $state.contains() as it's used a lot
31527 includes: function(state) {
31528 var includes = state.parent ? extend({}, state.parent.includes) : {};
31529 includes[state.name] = true;
31536 function isRelative(stateName) {
31537 return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
31540 function findState(stateOrName, base) {
31541 if (!stateOrName) return undefined;
31543 var isStr = isString(stateOrName),
31544 name = isStr ? stateOrName : stateOrName.name,
31545 path = isRelative(name);
31548 if (!base) throw new Error("No reference point given for path '" + name + "'");
31549 base = findState(base);
31551 var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
31553 for (; i < pathLength; i++) {
31554 if (rel[i] === "" && i === 0) {
31558 if (rel[i] === "^") {
31559 if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
31560 current = current.parent;
31565 rel = rel.slice(i).join(".");
31566 name = current.name + (current.name && rel ? "." : "") + rel;
31568 var state = states[name];
31570 if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
31576 function queueState(parentName, state) {
31577 if (!queue[parentName]) {
31578 queue[parentName] = [];
31580 queue[parentName].push(state);
31583 function flushQueuedChildren(parentName) {
31584 var queued = queue[parentName] || [];
31585 while(queued.length) {
31586 registerState(queued.shift());
31590 function registerState(state) {
31591 // Wrap a new object around the state so we can store our private details easily.
31592 state = inherit(state, {
31594 resolve: state.resolve || {},
31595 toString: function() { return this.name; }
31598 var name = state.name;
31599 if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
31601 if (states.hasOwnProperty(name)) throw new Error("State '" + name + "' is already defined");
31603 if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
31604 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31607 var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
31608 : (isString(state.parent)) ? state.parent
31609 : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
31612 // If parent is not registered yet, add state to queue and register later
31613 if (parentName && !states[parentName]) {
31614 return queueState(parentName, state.self);
31617 for (var key in stateBuilder) {
31618 if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
31620 states[name] = state;
31622 // Register the state in the global state list and with $urlRouter if necessary.
31623 if (!state[abstractKey] && state.url) {
31624 $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
31625 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
31626 $state.transitionTo(state, $match, { inherit: true, location: false });
31631 // Register any queued children
31632 flushQueuedChildren(name);
31637 // Checks text to see if it looks like a glob.
31638 function isGlob (text) {
31639 return text.indexOf('*') > -1;
31642 // Returns true if glob matches current $state name.
31643 function doesStateMatchGlob (glob) {
31644 var globSegments = glob.split('.'),
31645 segments = $state.$current.name.split('.');
31647 //match single stars
31648 for (var i = 0, l = globSegments.length; i < l; i++) {
31649 if (globSegments[i] === '*') {
31654 //match greedy starts
31655 if (globSegments[0] === '**') {
31656 segments = segments.slice(indexOf(segments, globSegments[1]));
31657 segments.unshift('**');
31659 //match greedy ends
31660 if (globSegments[globSegments.length - 1] === '**') {
31661 segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
31662 segments.push('**');
31665 if (globSegments.length != segments.length) {
31669 return segments.join('') === globSegments.join('');
31673 // Implicit root state that is always active
31674 root = registerState({
31680 root.navigable = null;
31685 * @name ui.router.state.$stateProvider#decorator
31686 * @methodOf ui.router.state.$stateProvider
31689 * Allows you to extend (carefully) or override (at your own peril) the
31690 * `stateBuilder` object used internally by `$stateProvider`. This can be used
31691 * to add custom functionality to ui-router, for example inferring templateUrl
31692 * based on the state name.
31694 * When passing only a name, it returns the current (original or decorated) builder
31695 * function that matches `name`.
31697 * The builder functions that can be decorated are listed below. Though not all
31698 * necessarily have a good use case for decoration, that is up to you to decide.
31700 * In addition, users can attach custom decorators, which will generate new
31701 * properties within the state's internal definition. There is currently no clear
31702 * use-case for this beyond accessing internal states (i.e. $state.$current),
31703 * however, expect this to become increasingly relevant as we introduce additional
31704 * meta-programming features.
31706 * **Warning**: Decorators should not be interdependent because the order of
31707 * execution of the builder functions in non-deterministic. Builder functions
31708 * should only be dependent on the state definition object and super function.
31711 * Existing builder functions and current return values:
31713 * - **parent** `{object}` - returns the parent state object.
31714 * - **data** `{object}` - returns state data, including any inherited data that is not
31715 * overridden by own values (if any).
31716 * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
31718 * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
31720 * - **params** `{object}` - returns an array of state params that are ensured to
31721 * be a super-set of parent's params.
31722 * - **views** `{object}` - returns a views object where each key is an absolute view
31723 * name (i.e. "viewName@stateName") and each value is the config object
31724 * (template, controller) for the view. Even when you don't use the views object
31725 * explicitly on a state config, one is still created for you internally.
31726 * So by decorating this builder function you have access to decorating template
31727 * and controller properties.
31728 * - **ownParams** `{object}` - returns an array of params that belong to the state,
31729 * not including any params defined by ancestor states.
31730 * - **path** `{string}` - returns the full path from the root down to this state.
31731 * Needed for state activation.
31732 * - **includes** `{object}` - returns an object that includes every state that
31733 * would pass a `$state.includes()` test.
31737 * // Override the internal 'views' builder with a function that takes the state
31738 * // definition, and a reference to the internal function being overridden:
31739 * $stateProvider.decorator('views', function (state, parent) {
31741 * views = parent(state);
31743 * angular.forEach(views, function (config, name) {
31744 * var autoName = (state.name + '.' + name).replace('.', '/');
31745 * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
31746 * result[name] = config;
31751 * $stateProvider.state('home', {
31753 * 'contact.list': { controller: 'ListController' },
31754 * 'contact.item': { controller: 'ItemController' }
31760 * $state.go('home');
31761 * // Auto-populates list and item views with /partials/home/contact/list.html,
31762 * // and /partials/home/contact/item.html, respectively.
31765 * @param {string} name The name of the builder function to decorate.
31766 * @param {object} func A function that is responsible for decorating the original
31767 * builder function. The function receives two parameters:
31769 * - `{object}` - state - The state config object.
31770 * - `{object}` - super - The original builder function.
31772 * @return {object} $stateProvider - $stateProvider instance
31774 this.decorator = decorator;
31775 function decorator(name, func) {
31776 /*jshint validthis: true */
31777 if (isString(name) && !isDefined(func)) {
31778 return stateBuilder[name];
31780 if (!isFunction(func) || !isString(name)) {
31783 if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
31784 stateBuilder.$delegates[name] = stateBuilder[name];
31786 stateBuilder[name] = func;
31792 * @name ui.router.state.$stateProvider#state
31793 * @methodOf ui.router.state.$stateProvider
31796 * Registers a state configuration under a given state name. The stateConfig object
31797 * has the following acceptable properties.
31799 * @param {string} name A unique state name, e.g. "home", "about", "contacts".
31800 * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
31801 * @param {object} stateConfig State configuration object.
31802 * @param {string|function=} stateConfig.template
31803 * <a id='template'></a>
31804 * html template as a string or a function that returns
31805 * an html template as a string which should be used by the uiView directives. This property
31806 * takes precedence over templateUrl.
31808 * If `template` is a function, it will be called with the following parameters:
31810 * - {array.<object>} - state parameters extracted from the current $location.path() by
31811 * applying the current state
31814 * "<h1>inline template definition</h1>" +
31815 * "<div ui-view></div>"</pre>
31816 * <pre>template: function(params) {
31817 * return "<h1>generated template</h1>"; }</pre>
31820 * @param {string|function=} stateConfig.templateUrl
31821 * <a id='templateUrl'></a>
31823 * path or function that returns a path to an html
31824 * template that should be used by uiView.
31826 * If `templateUrl` is a function, it will be called with the following parameters:
31828 * - {array.<object>} - state parameters extracted from the current $location.path() by
31829 * applying the current state
31831 * <pre>templateUrl: "home.html"</pre>
31832 * <pre>templateUrl: function(params) {
31833 * return myTemplates[params.pageId]; }</pre>
31835 * @param {function=} stateConfig.templateProvider
31836 * <a id='templateProvider'></a>
31837 * Provider function that returns HTML content string.
31838 * <pre> templateProvider:
31839 * function(MyTemplateService, params) {
31840 * return MyTemplateService.getTemplate(params.pageId);
31843 * @param {string|function=} stateConfig.controller
31844 * <a id='controller'></a>
31846 * Controller fn that should be associated with newly
31847 * related scope or the name of a registered controller if passed as a string.
31848 * Optionally, the ControllerAs may be declared here.
31849 * <pre>controller: "MyRegisteredController"</pre>
31851 * "MyRegisteredController as fooCtrl"}</pre>
31852 * <pre>controller: function($scope, MyService) {
31853 * $scope.data = MyService.getData(); }</pre>
31855 * @param {function=} stateConfig.controllerProvider
31856 * <a id='controllerProvider'></a>
31858 * Injectable provider function that returns the actual controller or string.
31859 * <pre>controllerProvider:
31860 * function(MyResolveData) {
31861 * if (MyResolveData.foo)
31863 * else if (MyResolveData.bar)
31864 * return "BarCtrl";
31865 * else return function($scope) {
31866 * $scope.baz = "Qux";
31870 * @param {string=} stateConfig.controllerAs
31871 * <a id='controllerAs'></a>
31873 * A controller alias name. If present the controller will be
31874 * published to scope under the controllerAs name.
31875 * <pre>controllerAs: "myCtrl"</pre>
31877 * @param {string|object=} stateConfig.parent
31878 * <a id='parent'></a>
31879 * Optionally specifies the parent state of this state.
31881 * <pre>parent: 'parentState'</pre>
31882 * <pre>parent: parentState // JS variable</pre>
31884 * @param {object=} stateConfig.resolve
31885 * <a id='resolve'></a>
31887 * An optional map<string, function> of dependencies which
31888 * should be injected into the controller. If any of these dependencies are promises,
31889 * the router will wait for them all to be resolved before the controller is instantiated.
31890 * If all the promises are resolved successfully, the $stateChangeSuccess event is fired
31891 * and the values of the resolved promises are injected into any controllers that reference them.
31892 * If any of the promises are rejected the $stateChangeError event is fired.
31894 * The map object is:
31896 * - key - {string}: name of dependency to be injected into controller
31897 * - factory - {string|function}: If string then it is alias for service. Otherwise if function,
31898 * it is injected and return value it treated as dependency. If result is a promise, it is
31899 * resolved before its value is injected into controller.
31903 * function($http, $stateParams) {
31904 * return $http.get("/api/foos/"+stateParams.fooID);
31908 * @param {string=} stateConfig.url
31911 * A url fragment with optional parameters. When a state is navigated or
31912 * transitioned to, the `$stateParams` service will be populated with any
31913 * parameters that were passed.
31915 * (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
31916 * more details on acceptable patterns )
31919 * <pre>url: "/home"
31920 * url: "/users/:userid"
31921 * url: "/books/{bookid:[a-zA-Z_-]}"
31922 * url: "/books/{categoryid:int}"
31923 * url: "/books/{publishername:string}/{categoryid:int}"
31924 * url: "/messages?before&after"
31925 * url: "/messages?{before:date}&{after:date}"
31926 * url: "/messages/:mailboxid?{before:date}&{after:date}"
31929 * @param {object=} stateConfig.views
31930 * <a id='views'></a>
31931 * an optional map<string, object> which defined multiple views, or targets views
31932 * manually/explicitly.
31936 * Targets three named `ui-view`s in the parent state's template
31939 * controller: "headerCtrl",
31940 * templateUrl: "header.html"
31942 * controller: "bodyCtrl",
31943 * templateUrl: "body.html"
31945 * controller: "footCtrl",
31946 * templateUrl: "footer.html"
31950 * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
31953 * controller: "msgHeaderCtrl",
31954 * templateUrl: "msgHeader.html"
31956 * controller: "messagesCtrl",
31957 * templateUrl: "messages.html"
31961 * @param {boolean=} [stateConfig.abstract=false]
31962 * <a id='abstract'></a>
31963 * An abstract state will never be directly activated,
31964 * but can provide inherited properties to its common children states.
31965 * <pre>abstract: true</pre>
31967 * @param {function=} stateConfig.onEnter
31968 * <a id='onEnter'></a>
31970 * Callback function for when a state is entered. Good way
31971 * to trigger an action or dispatch an event, such as opening a dialog.
31973 * If minifying your scripts, make sure to explicitly annotate this function,
31975 * If minifying your scripts, make sure to explictly annotate this function,
31976 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31977 * because it won't be automatically annotated by your build tools.
31979 * <pre>onEnter: function(MyService, $stateParams) {
31980 * MyService.foo($stateParams.myParam);
31983 * @param {function=} stateConfig.onExit
31984 * <a id='onExit'></a>
31986 * Callback function for when a state is exited. Good way to
31987 * trigger an action or dispatch an event, such as opening a dialog.
31989 * If minifying your scripts, make sure to explicitly annotate this function,
31991 * If minifying your scripts, make sure to explictly annotate this function,
31992 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
31993 * because it won't be automatically annotated by your build tools.
31995 * <pre>onExit: function(MyService, $stateParams) {
31996 * MyService.cleanup($stateParams.myParam);
31999 * @param {boolean=} [stateConfig.reloadOnSearch=true]
32000 * <a id='reloadOnSearch'></a>
32002 * If `false`, will not retrigger the same state
32003 * just because a search/query parameter has changed (via $location.search() or $location.hash()).
32004 * Useful for when you'd like to modify $location.search() without triggering a reload.
32005 * <pre>reloadOnSearch: false</pre>
32007 * @param {object=} stateConfig.data
32008 * <a id='data'></a>
32010 * Arbitrary data object, useful for custom configuration. The parent state's `data` is
32011 * prototypally inherited. In other words, adding a data property to a state adds it to
32012 * the entire subtree via prototypal inheritance.
32015 * requiredRole: 'foo'
32018 * @param {object=} stateConfig.params
32019 * <a id='params'></a>
32021 * A map which optionally configures parameters declared in the `url`, or
32022 * defines additional non-url parameters. For each parameter being
32023 * configured, add a configuration object keyed to the name of the parameter.
32025 * Each parameter configuration object may contain the following properties:
32027 * - ** value ** - {object|function=}: specifies the default value for this
32028 * parameter. This implicitly sets this parameter as optional.
32030 * When UI-Router routes to a state and no value is
32031 * specified for this parameter in the URL or transition, the
32032 * default value will be used instead. If `value` is a function,
32033 * it will be injected and invoked, and the return value used.
32035 * *Note*: `undefined` is treated as "no default value" while `null`
32036 * is treated as "the default value is `null`".
32038 * *Shorthand*: If you only need to configure the default value of the
32039 * parameter, you may use a shorthand syntax. In the **`params`**
32040 * map, instead mapping the param name to a full parameter configuration
32041 * object, simply set map it to the default parameter value, e.g.:
32043 * <pre>// define a parameter's default value
32045 * param1: { value: "defaultValue" }
32047 * // shorthand default values
32049 * param1: "defaultValue",
32050 * param2: "param2Default"
32053 * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
32054 * treated as an array of values. If you specified a Type, the value will be
32055 * treated as an array of the specified Type. Note: query parameter values
32056 * default to a special `"auto"` mode.
32058 * For query parameters in `"auto"` mode, if multiple values for a single parameter
32059 * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
32060 * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if
32061 * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
32062 * value (e.g.: `{ foo: '1' }`).
32065 * param1: { array: true }
32068 * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
32069 * the current parameter value is the same as the default value. If `squash` is not set, it uses the
32070 * configured default squash policy.
32071 * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
32073 * There are three squash settings:
32075 * - false: The parameter's default value is not squashed. It is encoded and included in the URL
32076 * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed
32077 * by slashes in the state's `url` declaration, then one of those slashes are omitted.
32078 * This can allow for cleaner looking URLs.
32079 * - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of your choice.
32083 * value: "defaultId",
32086 * // squash "defaultValue" to "~"
32089 * value: "defaultValue",
32097 * // Some state name examples
32099 * // stateName can be a single top-level name (must be unique).
32100 * $stateProvider.state("home", {});
32102 * // Or it can be a nested state name. This state is a child of the
32103 * // above "home" state.
32104 * $stateProvider.state("home.newest", {});
32106 * // Nest states as deeply as needed.
32107 * $stateProvider.state("home.newest.abc.xyz.inception", {});
32109 * // state() returns $stateProvider, so you can chain state declarations.
32111 * .state("home", {})
32112 * .state("about", {})
32113 * .state("contacts", {});
32117 this.state = state;
32118 function state(name, definition) {
32119 /*jshint validthis: true */
32120 if (isObject(name)) definition = name;
32121 else definition.name = name;
32122 registerState(definition);
32128 * @name ui.router.state.$state
32130 * @requires $rootScope
32132 * @requires ui.router.state.$view
32133 * @requires $injector
32134 * @requires ui.router.util.$resolve
32135 * @requires ui.router.state.$stateParams
32136 * @requires ui.router.router.$urlRouter
32138 * @property {object} params A param object, e.g. {sectionId: section.id)}, that
32139 * you'd like to test against the current active state.
32140 * @property {object} current A reference to the state's config object. However
32141 * you passed it in. Useful for accessing custom data.
32142 * @property {object} transition Currently pending transition. A promise that'll
32143 * resolve or reject.
32146 * `$state` service is responsible for representing states as well as transitioning
32147 * between them. It also provides interfaces to ask for current state or even states
32148 * you're coming from.
32151 $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
32152 function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) {
32154 var TransitionSuperseded = $q.reject(new Error('transition superseded'));
32155 var TransitionPrevented = $q.reject(new Error('transition prevented'));
32156 var TransitionAborted = $q.reject(new Error('transition aborted'));
32157 var TransitionFailed = $q.reject(new Error('transition failed'));
32159 // Handles the case where a state which is the target of a transition is not found, and the user
32160 // can optionally retry or defer the transition
32161 function handleRedirect(redirect, state, params, options) {
32164 * @name ui.router.state.$state#$stateNotFound
32165 * @eventOf ui.router.state.$state
32166 * @eventType broadcast on root scope
32168 * Fired when a requested state **cannot be found** using the provided state name during transition.
32169 * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
32170 * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
32171 * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
32172 * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
32174 * @param {Object} event Event object.
32175 * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
32176 * @param {State} fromState Current state object.
32177 * @param {Object} fromParams Current state params.
32182 * // somewhere, assume lazy.state has not been defined
32183 * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
32185 * // somewhere else
32186 * $scope.$on('$stateNotFound',
32187 * function(event, unfoundState, fromState, fromParams){
32188 * console.log(unfoundState.to); // "lazy.state"
32189 * console.log(unfoundState.toParams); // {a:1, b:2}
32190 * console.log(unfoundState.options); // {inherit:false} + default options
32194 var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
32196 if (evt.defaultPrevented) {
32197 $urlRouter.update();
32198 return TransitionAborted;
32205 // Allow the handler to return a promise to defer state lookup retry
32206 if (options.$retry) {
32207 $urlRouter.update();
32208 return TransitionFailed;
32210 var retryTransition = $state.transition = $q.when(evt.retry);
32212 retryTransition.then(function() {
32213 if (retryTransition !== $state.transition) return TransitionSuperseded;
32214 redirect.options.$retry = true;
32215 return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
32217 return TransitionAborted;
32219 $urlRouter.update();
32221 return retryTransition;
32224 root.locals = { resolve: null, globals: { $stateParams: {} } };
32228 current: root.self,
32235 * @name ui.router.state.$state#reload
32236 * @methodOf ui.router.state.$state
32239 * A method that force reloads the current state. All resolves are re-resolved,
32240 * controllers reinstantiated, and events re-fired.
32244 * var app angular.module('app', ['ui.router']);
32246 * app.controller('ctrl', function ($scope, $state) {
32247 * $scope.reload = function(){
32253 * `reload()` is just an alias for:
32255 * $state.transitionTo($state.current, $stateParams, {
32256 * reload: true, inherit: false, notify: true
32260 * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
32263 * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item'
32264 * //and current state is 'contacts.detail.item'
32265 * var app angular.module('app', ['ui.router']);
32267 * app.controller('ctrl', function ($scope, $state) {
32268 * $scope.reload = function(){
32269 * //will reload 'contact.detail' and 'contact.detail.item' states
32270 * $state.reload('contact.detail');
32275 * `reload()` is just an alias for:
32277 * $state.transitionTo($state.current, $stateParams, {
32278 * reload: true, inherit: false, notify: true
32282 * @returns {promise} A promise representing the state of the new transition. See
32283 * {@link ui.router.state.$state#methods_go $state.go}.
32285 $state.reload = function reload(state) {
32286 return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
32291 * @name ui.router.state.$state#go
32292 * @methodOf ui.router.state.$state
32295 * Convenience method for transitioning to a new state. `$state.go` calls
32296 * `$state.transitionTo` internally but automatically sets options to
32297 * `{ location: true, inherit: true, relative: $state.$current, notify: true }`.
32298 * This allows you to easily use an absolute or relative to path and specify
32299 * only the parameters you'd like to update (while letting unspecified parameters
32300 * inherit from the currently active ancestor states).
32304 * var app = angular.module('app', ['ui.router']);
32306 * app.controller('ctrl', function ($scope, $state) {
32307 * $scope.changeState = function () {
32308 * $state.go('contact.detail');
32312 * <img src='../ngdoc_assets/StateGoExamples.png'/>
32314 * @param {string} to Absolute state name or relative state path. Some examples:
32316 * - `$state.go('contact.detail')` - will go to the `contact.detail` state
32317 * - `$state.go('^')` - will go to a parent state
32318 * - `$state.go('^.sibling')` - will go to a sibling state
32319 * - `$state.go('.child.grandchild')` - will go to grandchild state
32321 * @param {object=} params A map of the parameters that will be sent to the state,
32322 * will populate $stateParams. Any parameters that are not specified will be inherited from currently
32324 * defined parameters. Only parameters specified in the state definition can be overridden, new
32325 * parameters will be ignored. This allows, for example, going to a sibling state that shares parameters
32327 * defined parameters. This allows, for example, going to a sibling state that shares parameters
32328 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32329 * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
32330 * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
32331 * will get you all current parameters, etc.
32332 * @param {object=} options Options object. The options are:
32334 * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32335 * will not. If string, must be `"replace"`, which will update url and also replace last history record.
32336 * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32337 * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
32338 * defines which state to be relative from.
32339 * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32341 * - **`reload`** (v0.2.5) - {boolean=false|string|object}, If `true` will force transition even if no state or params
32342 * have changed. It will reload the resolves and views of the current state and parent states.
32343 * If `reload` is a string (or state object), the state object is fetched (by name, or object reference); and \
32344 * the transition reloads the resolves and views for that matched state, and all its children states.
32346 * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
32347 * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32348 * use this when you want to force a reload when *everything* is the same, including search params.
32349 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32351 * @returns {promise} A promise representing the state of the new transition.
32353 * Possible success values:
32357 * <br/>Possible rejection values:
32359 * - 'transition superseded' - when a newer transition has been started after this one
32360 * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
32361 * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
32362 * when a `$stateNotFound` `event.retry` promise errors.
32363 * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
32364 * - *resolve error* - when an error has occurred with a `resolve`
32367 $state.go = function go(to, params, options) {
32368 return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
32373 * @name ui.router.state.$state#transitionTo
32374 * @methodOf ui.router.state.$state
32377 * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
32378 * uses `transitionTo` internally. `$state.go` is recommended in most situations.
32382 * var app = angular.module('app', ['ui.router']);
32384 * app.controller('ctrl', function ($scope, $state) {
32385 * $scope.changeState = function () {
32386 * $state.transitionTo('contact.detail');
32391 * @param {string} to State name.
32392 * @param {object=} toParams A map of the parameters that will be sent to the state,
32393 * will populate $stateParams.
32394 * @param {object=} options Options object. The options are:
32396 * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32397 * will not. If string, must be `"replace"`, which will update url and also replace last history record.
32398 * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
32399 * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'),
32400 * defines which state to be relative from.
32401 * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32402 * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params
32403 * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32404 * use this when you want to force a reload when *everything* is the same, including search params.
32405 * if String, then will reload the state with the name given in reload, and any children.
32406 * if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
32408 * @returns {promise} A promise representing the state of the new transition. See
32409 * {@link ui.router.state.$state#methods_go $state.go}.
32411 $state.transitionTo = function transitionTo(to, toParams, options) {
32412 toParams = toParams || {};
32414 location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
32417 var from = $state.$current, fromParams = $state.params, fromPath = from.path;
32418 var evt, toState = findState(to, options.relative);
32420 // Store the hash param for later (since it will be stripped out by various methods)
32421 var hash = toParams['#'];
32423 if (!isDefined(toState)) {
32424 var redirect = { to: to, toParams: toParams, options: options };
32425 var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
32427 if (redirectResult) {
32428 return redirectResult;
32431 // Always retry once if the $stateNotFound was not prevented
32432 // (handles either redirect changed or state lazy-definition)
32434 toParams = redirect.toParams;
32435 options = redirect.options;
32436 toState = findState(to, options.relative);
32438 if (!isDefined(toState)) {
32439 if (!options.relative) throw new Error("No such state '" + to + "'");
32440 throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
32443 if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
32444 if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
32445 if (!toState.params.$$validates(toParams)) return TransitionFailed;
32447 toParams = toState.params.$$values(toParams);
32450 var toPath = to.path;
32452 // Starting from the root of the path, keep all levels that haven't changed
32453 var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
32455 if (!options.reload) {
32456 while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
32457 locals = toLocals[keep] = state.locals;
32459 state = toPath[keep];
32461 } else if (isString(options.reload) || isObject(options.reload)) {
32462 if (isObject(options.reload) && !options.reload.name) {
32463 throw new Error('Invalid reload state object');
32466 var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
32467 if (options.reload && !reloadState) {
32468 throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
32471 while (state && state === fromPath[keep] && state !== reloadState) {
32472 locals = toLocals[keep] = state.locals;
32474 state = toPath[keep];
32478 // If we're going to the same state and all locals are kept, we've got nothing to do.
32479 // But clear 'transition', as we still want to cancel any other pending transitions.
32480 // TODO: We may not want to bump 'transition' if we're called from a location change
32481 // that we've initiated ourselves, because we might accidentally abort a legitimate
32482 // transition initiated from code?
32483 if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
32484 if (hash) toParams['#'] = hash;
32485 $state.params = toParams;
32486 copy($state.params, $stateParams);
32488 copy(filterByKeys(to.params.$$keys(), $stateParams), to.locals.globals.$stateParams);
32490 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32491 if (options.location && to.navigable && to.navigable.url) {
32492 $urlRouter.push(to.navigable.url, toParams, {
32493 $$avoidResync: true, replace: options.location === 'replace'
32495 $urlRouter.update(true);
32497 $state.transition = null;
32498 return $q.when($state.current);
32501 // Filter parameters before we pass them to event handlers etc.
32502 toParams = filterByKeys(to.params.$$keys(), toParams || {});
32505 // Re-add the saved hash before we start returning things or broadcasting $stateChangeStart
32506 if (hash) toParams['#'] = hash;
32510 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32511 // Broadcast start event and cancel the transition if requested
32512 if (options.notify) {
32515 * @name ui.router.state.$state#$stateChangeStart
32516 * @eventOf ui.router.state.$state
32517 * @eventType broadcast on root scope
32519 * Fired when the state transition **begins**. You can use `event.preventDefault()`
32520 * to prevent the transition from happening and then the transition promise will be
32521 * rejected with a `'transition prevented'` value.
32523 * @param {Object} event Event object.
32524 * @param {State} toState The state being transitioned to.
32525 * @param {Object} toParams The params supplied to the `toState`.
32526 * @param {State} fromState The current state, pre-transition.
32527 * @param {Object} fromParams The params supplied to the `fromState`.
32532 * $rootScope.$on('$stateChangeStart',
32533 * function(event, toState, toParams, fromState, fromParams){
32534 * event.preventDefault();
32535 * // transitionTo() promise will be rejected with
32536 * // a 'transition prevented' error
32541 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams, options).defaultPrevented) {
32542 $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32543 //Don't update and resync url if there's been a new transition started. see issue #2238, #600
32544 if ($state.transition == null) $urlRouter.update();
32546 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
32547 $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32548 $urlRouter.update();
32549 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32550 return TransitionPrevented;
32554 // Resolve locals for the remaining states, but don't update any global state just
32555 // yet -- if anything fails to resolve the current state needs to remain untouched.
32556 // We also set up an inheritance chain for the locals here. This allows the view directive
32557 // to quickly look up the correct definition for each view in the current state. Even
32558 // though we create the locals object itself outside resolveState(), it is initially
32559 // empty and gets filled asynchronously. We need to keep track of the promise for the
32560 // (fully resolved) current locals, and pass this down the chain.
32561 var resolved = $q.when(locals);
32563 for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
32564 locals = toLocals[l] = inherit(locals);
32565 resolved = resolveState(state, toParams, state === to, resolved, locals, options);
32568 // Once everything is resolved, we are ready to perform the actual transition
32569 // and return a promise for the new state. We also keep track of what the
32570 // current promise is, so that we can detect overlapping transitions and
32571 // keep only the outcome of the last transition.
32572 var transition = $state.transition = resolved.then(function () {
32573 var l, entering, exiting;
32575 if ($state.transition !== transition) return TransitionSuperseded;
32577 // Exit 'from' states not kept
32578 for (l = fromPath.length - 1; l >= keep; l--) {
32579 exiting = fromPath[l];
32580 if (exiting.self.onExit) {
32581 $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
32583 exiting.locals = null;
32586 // Enter 'to' states not kept
32587 for (l = keep; l < toPath.length; l++) {
32588 entering = toPath[l];
32589 entering.locals = toLocals[l];
32590 if (entering.self.onEnter) {
32591 $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
32597 // Re-add the saved hash before we start returning things
32598 if (hash) toParams['#'] = hash;
32600 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32601 // Run it again, to catch any transitions in callbacks
32602 if ($state.transition !== transition) return TransitionSuperseded;
32604 // Update globals in $state
32605 $state.$current = to;
32606 $state.current = to.self;
32607 $state.params = toParams;
32608 copy($state.params, $stateParams);
32609 $state.transition = null;
32611 if (options.location && to.navigable) {
32612 $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
32613 $$avoidResync: true, replace: options.location === 'replace'
32617 if (options.notify) {
32620 * @name ui.router.state.$state#$stateChangeSuccess
32621 * @eventOf ui.router.state.$state
32622 * @eventType broadcast on root scope
32624 * Fired once the state transition is **complete**.
32626 * @param {Object} event Event object.
32627 * @param {State} toState The state being transitioned to.
32628 * @param {Object} toParams The params supplied to the `toState`.
32629 * @param {State} fromState The current state, pre-transition.
32630 * @param {Object} fromParams The params supplied to the `fromState`.
32632 $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
32634 $urlRouter.update(true);
32636 return $state.current;
32637 }, function (error) {
32638 if ($state.transition !== transition) return TransitionSuperseded;
32640 $state.transition = null;
32643 * @name ui.router.state.$state#$stateChangeError
32644 * @eventOf ui.router.state.$state
32645 * @eventType broadcast on root scope
32647 * Fired when an **error occurs** during transition. It's important to note that if you
32648 * have any errors in your resolve functions (javascript errors, non-existent services, etc)
32649 * they will not throw traditionally. You must listen for this $stateChangeError event to
32650 * catch **ALL** errors.
32652 * @param {Object} event Event object.
32653 * @param {State} toState The state being transitioned to.
32654 * @param {Object} toParams The params supplied to the `toState`.
32655 * @param {State} fromState The current state, pre-transition.
32656 * @param {Object} fromParams The params supplied to the `fromState`.
32657 * @param {Error} error The resolve error object.
32659 evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
32661 if (!evt.defaultPrevented) {
32662 $urlRouter.update();
32665 return $q.reject(error);
32673 * @name ui.router.state.$state#is
32674 * @methodOf ui.router.state.$state
32677 * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
32678 * but only checks for the full state name. If params is supplied then it will be
32679 * tested for strict equality against the current active params object, so all params
32680 * must match with none missing and no extras.
32684 * $state.$current.name = 'contacts.details.item';
32687 * $state.is('contact.details.item'); // returns true
32688 * $state.is(contactDetailItemStateObject); // returns true
32690 * // relative name (. and ^), typically from a template
32691 * // E.g. from the 'contacts.details' template
32692 * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
32695 * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
32696 * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
32697 * to test against the current active state.
32698 * @param {object=} options An options object. The options are:
32700 * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will
32701 * test relative to `options.relative` state (or name).
32703 * @returns {boolean} Returns true if it is the state.
32705 $state.is = function is(stateOrName, params, options) {
32706 options = extend({ relative: $state.$current }, options || {});
32707 var state = findState(stateOrName, options.relative);
32709 if (!isDefined(state)) { return undefined; }
32710 if ($state.$current !== state) { return false; }
32711 return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
32716 * @name ui.router.state.$state#includes
32717 * @methodOf ui.router.state.$state
32720 * A method to determine if the current active state is equal to or is the child of the
32721 * state stateName. If any params are passed then they will be tested for a match as well.
32722 * Not all the parameters need to be passed, just the ones you'd like to test for equality.
32725 * Partial and relative names
32727 * $state.$current.name = 'contacts.details.item';
32729 * // Using partial names
32730 * $state.includes("contacts"); // returns true
32731 * $state.includes("contacts.details"); // returns true
32732 * $state.includes("contacts.details.item"); // returns true
32733 * $state.includes("contacts.list"); // returns false
32734 * $state.includes("about"); // returns false
32736 * // Using relative names (. and ^), typically from a template
32737 * // E.g. from the 'contacts.details' template
32738 * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
32741 * Basic globbing patterns
32743 * $state.$current.name = 'contacts.details.item.url';
32745 * $state.includes("*.details.*.*"); // returns true
32746 * $state.includes("*.details.**"); // returns true
32747 * $state.includes("**.item.**"); // returns true
32748 * $state.includes("*.details.item.url"); // returns true
32749 * $state.includes("*.details.*.url"); // returns true
32750 * $state.includes("*.details.*"); // returns false
32751 * $state.includes("item.**"); // returns false
32754 * @param {string} stateOrName A partial name, relative name, or glob pattern
32755 * to be searched for within the current state name.
32756 * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
32757 * that you'd like to test against the current active state.
32758 * @param {object=} options An options object. The options are:
32760 * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set,
32761 * .includes will test relative to `options.relative` state (or name).
32763 * @returns {boolean} Returns true if it does include the state
32765 $state.includes = function includes(stateOrName, params, options) {
32766 options = extend({ relative: $state.$current }, options || {});
32767 if (isString(stateOrName) && isGlob(stateOrName)) {
32768 if (!doesStateMatchGlob(stateOrName)) {
32771 stateOrName = $state.$current.name;
32774 var state = findState(stateOrName, options.relative);
32775 if (!isDefined(state)) { return undefined; }
32776 if (!isDefined($state.$current.includes[state.name])) { return false; }
32777 return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
32783 * @name ui.router.state.$state#href
32784 * @methodOf ui.router.state.$state
32787 * A url generation method that returns the compiled url for the given state populated with the given params.
32791 * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
32794 * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
32795 * @param {object=} params An object of parameter values to fill the state's required parameters.
32796 * @param {object=} options Options object. The options are:
32798 * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the
32799 * first parameter, then the constructed href url will be built from the first navigable ancestor (aka
32800 * ancestor with a valid url).
32801 * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32802 * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
32803 * defines which state to be relative from.
32804 * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
32806 * @returns {string} compiled state url
32808 $state.href = function href(stateOrName, params, options) {
32813 relative: $state.$current
32816 var state = findState(stateOrName, options.relative);
32818 if (!isDefined(state)) return null;
32819 if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
32821 var nav = (state && options.lossy) ? state.navigable : state;
32823 if (!nav || nav.url === undefined || nav.url === null) {
32826 return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
32827 absolute: options.absolute
32833 * @name ui.router.state.$state#get
32834 * @methodOf ui.router.state.$state
32837 * Returns the state configuration object for any specific state or all states.
32839 * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
32840 * the requested state. If not provided, returns an array of ALL state configs.
32841 * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
32842 * @returns {Object|Array} State configuration object or array of all objects.
32844 $state.get = function (stateOrName, context) {
32845 if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
32846 var state = findState(stateOrName, context || $state.$current);
32847 return (state && state.self) ? state.self : null;
32850 function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
32851 // Make a restricted $stateParams with only the parameters that apply to this state if
32852 // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
32853 // we also need $stateParams to be available for any $injector calls we make during the
32854 // dependency resolution process.
32855 var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
32856 var locals = { $stateParams: $stateParams };
32858 // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
32859 // We're also including $stateParams in this; that way the parameters are restricted
32860 // to the set that should be visible to the state, and are independent of when we update
32861 // the global $state and $stateParams values.
32862 dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
32863 var promises = [dst.resolve.then(function (globals) {
32864 dst.globals = globals;
32866 if (inherited) promises.push(inherited);
32868 function resolveViews() {
32869 var viewsPromises = [];
32871 // Resolve template and dependencies for all views.
32872 forEach(state.views, function (view, name) {
32873 var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
32874 injectables.$template = [ function () {
32875 return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
32878 viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
32879 // References to the controller (only instantiated at link time)
32880 if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
32881 var injectLocals = angular.extend({}, injectables, dst.globals);
32882 result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
32884 result.$$controller = view.controller;
32886 // Provide access to the state itself for internal use
32887 result.$$state = state;
32888 result.$$controllerAs = view.controllerAs;
32889 dst[name] = result;
32893 return $q.all(viewsPromises).then(function(){
32894 return dst.globals;
32898 // Wait for all the promises and then return the activation object
32899 return $q.all(promises).then(resolveViews).then(function (values) {
32907 function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
32908 // Return true if there are no differences in non-search (path/object) params, false if there are differences
32909 function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
32910 // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
32911 function notSearchParam(key) {
32912 return fromAndToState.params[key].location != "search";
32914 var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
32915 var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
32916 var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
32917 return nonQueryParamSet.$$equals(fromParams, toParams);
32920 // If reload was not explicitly requested
32921 // and we're transitioning to the same state we're already in
32922 // and the locals didn't change
32923 // or they changed in a way that doesn't merit reloading
32924 // (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
32925 // Then return true.
32926 if (!options.reload && to === from &&
32927 (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
32933 angular.module('ui.router.state')
32935 .factory('$stateParams', function () { return {}; })
32937 .value('$stateParams', {})
32938 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
32939 .provider('$state', $StateProvider);
32942 $ViewProvider.$inject = [];
32943 function $ViewProvider() {
32948 * @name ui.router.state.$view
32950 * @requires ui.router.util.$templateFactory
32951 * @requires $rootScope
32956 $get.$inject = ['$rootScope', '$templateFactory'];
32957 function $get( $rootScope, $templateFactory) {
32959 // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
32962 * @name ui.router.state.$view#load
32963 * @methodOf ui.router.state.$view
32967 * @param {string} name name
32968 * @param {object} options option object.
32970 load: function load(name, options) {
32971 var result, defaults = {
32972 template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
32974 options = extend(defaults, options);
32976 if (options.view) {
32977 result = $templateFactory.fromConfig(options.view, options.params, options.locals);
32981 if (result && options.notify) {
32984 * @name ui.router.state.$state#$viewContentLoading
32985 * @eventOf ui.router.state.$view
32986 * @eventType broadcast on root scope
32989 * Fired once the view **begins loading**, *before* the DOM is rendered.
32991 * @param {Object} event Event object.
32992 * @param {Object} viewConfig The view config properties (template, controller, etc).
32997 * $scope.$on('$viewContentLoading',
32998 * function(event, viewConfig){
32999 * // Access to all the view config properties.
33000 * // and one special property 'targetView'
33001 * // viewConfig.targetView
33005 $rootScope.$broadcast('$viewContentLoading', options);
33007 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33014 angular.module('ui.router.state').provider('$view', $ViewProvider);
33018 * @name ui.router.state.$uiViewScrollProvider
33021 * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
33023 function $ViewScrollProvider() {
33025 var useAnchorScroll = false;
33029 * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
33030 * @methodOf ui.router.state.$uiViewScrollProvider
33033 * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
33034 * scrolling based on the url anchor.
33036 this.useAnchorScroll = function () {
33037 useAnchorScroll = true;
33042 * @name ui.router.state.$uiViewScroll
33044 * @requires $anchorScroll
33045 * @requires $timeout
33048 * When called with a jqLite element, it scrolls the element into view (after a
33049 * `$timeout` so the DOM has time to refresh).
33051 * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
33052 * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
33054 this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
33055 if (useAnchorScroll) {
33056 return $anchorScroll;
33059 return function ($element) {
33060 return $timeout(function () {
33061 $element[0].scrollIntoView();
33067 angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
33070 var ngMajorVer = angular.version.major;
33071 var ngMinorVer = angular.version.minor;
33073 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33076 * @name ui.router.state.directive:ui-view
33078 * @requires ui.router.state.$state
33079 * @requires $compile
33080 * @requires $controller
33081 * @requires $injector
33082 * @requires ui.router.state.$uiViewScroll
33083 * @requires $document
33088 * The ui-view directive tells $state where to place your templates.
33090 * @param {string=} name A view name. The name should be unique amongst the other views in the
33091 * same state. You can have views of the same name that live in different states.
33093 * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
33094 * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
33095 * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
33096 * scroll ui-view elements into view when they are populated during a state activation.
33099 * @param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations
33100 * will be applied to the ui-view)
33103 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33104 * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
33105 * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
33107 * @param {string=} onload Expression to evaluate whenever the view updates.
33110 * A view can be unnamed or named.
33113 * <div ui-view></div>
33116 * <div ui-view="viewName"></div>
33119 * You can only have one unnamed view within any template (or root html). If you are only using a
33120 * single view and it is unnamed then you can populate it like so:
33122 * <div ui-view></div>
33123 * $stateProvider.state("home", {
33124 * template: "<h1>HELLO!</h1>"
33128 * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
33129 * config property, by name, in this case an empty name:
33131 * $stateProvider.state("home", {
33134 * template: "<h1>HELLO!</h1>"
33140 * But typically you'll only use the views property if you name your view or have more than one view
33141 * in the same template. There's not really a compelling reason to name a view if its the only one,
33142 * but you could if you wanted, like so:
33144 * <div ui-view="main"></div>
33147 * $stateProvider.state("home", {
33150 * template: "<h1>HELLO!</h1>"
33156 * Really though, you'll use views to set up multiple views:
33158 * <div ui-view></div>
33159 * <div ui-view="chart"></div>
33160 * <div ui-view="data"></div>
33164 * $stateProvider.state("home", {
33167 * template: "<h1>HELLO!</h1>"
33170 * template: "<chart_thing/>"
33173 * template: "<data_thing/>"
33179 * Examples for `autoscroll`:
33182 * <!-- If autoscroll present with no expression,
33183 * then scroll ui-view into view -->
33184 * <ui-view autoscroll/>
33186 * <!-- If autoscroll present with valid expression,
33187 * then scroll ui-view into view if expression evaluates to true -->
33188 * <ui-view autoscroll='true'/>
33189 * <ui-view autoscroll='false'/>
33190 * <ui-view autoscroll='scopeVariable'/>
33193 $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
33194 function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
33196 function getService() {
33197 return ($injector.has) ? function(service) {
33198 return $injector.has(service) ? $injector.get(service) : null;
33199 } : function(service) {
33201 return $injector.get(service);
33208 var service = getService(),
33209 $animator = service('$animator'),
33210 $animate = service('$animate');
33212 // Returns a set of DOM manipulation functions based on which Angular version
33214 function getRenderer(attrs, scope) {
33217 enter: function (element, target, cb) { target.after(element); cb(); },
33218 leave: function (element, cb) { element.remove(); cb(); }
33221 if (!!attrs.noanimation) return statics;
33223 function animEnabled(element) {
33224 if (ngMajorVer === 1 && ngMinorVer >= 4) return !!$animate.enabled(element);
33225 if (ngMajorVer === 1 && ngMinorVer >= 2) return !!$animate.enabled();
33226 return (!!$animator);
33232 enter: function(element, target, cb) {
33233 if (!animEnabled(element)) {
33234 statics.enter(element, target, cb);
33235 } else if (angular.version.minor > 2) {
33236 $animate.enter(element, null, target).then(cb);
33238 $animate.enter(element, null, target, cb);
33241 leave: function(element, cb) {
33242 if (!animEnabled(element)) {
33243 statics.leave(element, cb);
33244 } else if (angular.version.minor > 2) {
33245 $animate.leave(element).then(cb);
33247 $animate.leave(element, cb);
33250 var statics = function() {
33252 enter: function (element, target, cb) { target.after(element); cb(); },
33253 leave: function (element, cb) { element.remove(); cb(); }
33259 enter: function(element, target, cb) {
33260 var promise = $animate.enter(element, null, target, cb);
33261 if (promise && promise.then) promise.then(cb);
33263 leave: function(element, cb) {
33264 var promise = $animate.leave(element, cb);
33265 if (promise && promise.then) promise.then(cb);
33266 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33274 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33276 var animate = $animator && $animator(scope, attrs);
33279 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
33280 leave: function(element, cb) { animate.leave(element); cb(); }
33288 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33295 transclude: 'element',
33296 compile: function (tElement, tAttrs, $transclude) {
33297 return function (scope, $element, attrs) {
33298 var previousEl, currentEl, currentScope, latestLocals,
33299 onloadExp = attrs.onload || '',
33300 autoScrollExp = attrs.autoscroll,
33301 renderer = getRenderer(attrs, scope);
33303 scope.$on('$stateChangeSuccess', function() {
33308 scope.$on('$viewContentLoading', function() {
33311 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33315 function cleanupLastView() {
33317 var _previousEl = previousEl;
33318 var _currentScope = currentScope;
33320 if (_currentScope) {
33321 _currentScope._willBeDestroyed = true;
33324 function cleanOld() {
33326 _previousEl.remove();
33329 if (_currentScope) {
33330 _currentScope.$destroy();
33334 previousEl.remove();
33338 if (currentScope) {
33339 currentScope.$destroy();
33340 currentScope = null;
33341 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33345 renderer.leave(currentEl, function() {
33349 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33353 previousEl = currentEl;
33361 currentScope = null;
33365 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33368 function updateView(firstTime) {
33370 name = getUiViewName(scope, attrs, $element, $interpolate),
33371 previousLocals = name && $state.$current && $state.$current.locals[name];
33374 if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
33375 newScope = scope.$new();
33376 latestLocals = $state.$current.locals[name];
33380 * @name ui.router.state.directive:ui-view#$viewContentLoading
33381 * @eventOf ui.router.state.directive:ui-view
33382 * @eventType emits on ui-view directive scope
33385 * Fired once the view **begins loading**, *before* the DOM is rendered.
33387 * @param {Object} event Event object.
33388 * @param {string} viewName Name of the view.
33390 newScope.$emit('$viewContentLoading', name);
33393 if (!firstTime && previousLocals === latestLocals) return; // nothing to do
33394 newScope = scope.$new();
33395 latestLocals = $state.$current.locals[name];
33397 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33398 var clone = $transclude(newScope, function(clone) {
33399 renderer.enter(clone, $element, function onUiViewEnter() {
33401 currentScope.$emit('$viewContentAnimationEnded');
33404 if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
33405 $uiViewScroll(clone);
33412 currentScope = newScope;
33415 * @name ui.router.state.directive:ui-view#$viewContentLoaded
33416 * @eventOf ui.router.state.directive:ui-view
33417 * @eventType emits on ui-view directive scope
33420 * Fired once the view is **loaded**, *after* the DOM is rendered.
33422 * @param {Object} event Event object.
33423 * @param {string} viewName Name of the view.
33425 currentScope.$emit('$viewContentLoaded', name);
33428 * Fired once the view is **loaded**, *after* the DOM is rendered.
33430 * @param {Object} event Event object.
33432 currentScope.$emit('$viewContentLoaded');
33433 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33434 currentScope.$eval(onloadExp);
33443 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
33444 function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
33448 compile: function (tElement) {
33449 var initial = tElement.html();
33450 return function (scope, $element, attrs) {
33451 var current = $state.$current,
33452 name = getUiViewName(scope, attrs, $element, $interpolate),
33453 locals = current && current.locals[name];
33459 $element.data('$uiView', { name: name, state: locals.$$state });
33460 $element.html(locals.$template ? locals.$template : initial);
33462 var link = $compile($element.contents());
33464 if (locals.$$controller) {
33465 locals.$scope = scope;
33466 locals.$element = $element;
33467 var controller = $controller(locals.$$controller, locals);
33468 if (locals.$$controllerAs) {
33469 scope[locals.$$controllerAs] = controller;
33471 $element.data('$ngControllerController', controller);
33472 $element.children().data('$ngControllerController', controller);
33482 * Shared ui-view code for both directives:
33483 * Given scope, element, and its attributes, return the view's name
33485 function getUiViewName(scope, attrs, element, $interpolate) {
33486 var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
33487 var inherited = element.inheritedData('$uiView');
33488 return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
33491 angular.module('ui.router.state').directive('uiView', $ViewDirective);
33492 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
33494 function parseStateRef(ref, current) {
33495 var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
33496 if (preparsed) ref = current + '(' + preparsed[1] + ')';
33497 parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
33498 if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
33499 return { state: parsed[1], paramExpr: parsed[3] || null };
33502 function stateContext(el) {
33503 var stateData = el.parent().inheritedData('$uiView');
33505 if (stateData && stateData.state && stateData.state.name) {
33506 return stateData.state;
33511 function getTypeInfo(el) {
33512 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33513 var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
33514 var isForm = el[0].nodeName === "FORM";
33517 attr: isForm ? "action" : (isSvg ? 'xlink:href' : 'href'),
33518 isAnchor: el.prop("tagName").toUpperCase() === "A",
33523 function clickHook(el, $state, $timeout, type, current) {
33524 return function(e) {
33525 var button = e.which || e.button, target = current();
33527 if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) {
33528 // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33529 var transition = $timeout(function() {
33530 $state.go(target.state, target.params, target.options);
33532 e.preventDefault();
33534 // if the state has no URL, ignore one preventDefault from the <a> directive.
33535 var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1: 0;
33537 e.preventDefault = function() {
33538 if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition);
33544 function defaultOpts(el, $state) {
33545 return { relative: stateContext(el) || $state.$current, inherit: true };
33549 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33552 * @name ui.router.state.directive:ui-sref
33554 * @requires ui.router.state.$state
33555 * @requires $timeout
33561 * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
33562 * URL, the directive will automatically generate & update the `href` attribute via
33563 * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
33564 * the link will trigger a state transition with optional parameters.
33566 * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
33567 * handled natively by the browser.
33569 * You can also use relative state paths within ui-sref, just like the relative
33570 * paths passed to `$state.go()`. You just need to be aware that the path is relative
33571 * to the state that the link lives in, in other words the state that loaded the
33573 * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
33574 * URL, the directive will automatically generate & update the `href` attribute via
33575 * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
33576 * the link will trigger a state transition with optional parameters.
33578 * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
33579 * handled natively by the browser.
33581 * You can also use relative state paths within ui-sref, just like the relative
33582 * paths passed to `$state.go()`. You just need to be aware that the path is relative
33583 * to the state that the link lives in, in other words the state that loaded the
33584 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33585 * template containing the link.
33587 * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
33588 * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
33593 * Here's an example of how you'd use ui-sref and how it would compile. If you have the
33594 * following template:
33596 * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33599 * Here's an example of how you'd use ui-sref and how it would compile. If you have the
33600 * following template:
33602 * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33604 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33606 * <li ng-repeat="contact in contacts">
33607 * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
33613 * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33615 * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
33619 * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33621 * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
33623 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33625 * <li ng-repeat="contact in contacts">
33626 * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
33628 * <li ng-repeat="contact in contacts">
33629 * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
33631 * <li ng-repeat="contact in contacts">
33632 * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
33636 * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
33639 * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
33640 * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33642 $StateRefDirective.$inject = ['$state', '$timeout'];
33643 function $StateRefDirective($state, $timeout) {
33646 var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
33648 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33651 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33652 link: function(scope, element, attrs, uiSrefActive) {
33654 var ref = parseStateRef(attrs.uiSref, $state.current.name);
33655 var def = { state: ref.state, href: null, params: null };
33656 var type = getTypeInfo(element);
33657 var active = uiSrefActive[1] || uiSrefActive[0];
33659 def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {});
33661 var update = function(val) {
33662 if (val) def.params = angular.copy(val);
33663 def.href = $state.href(ref.state, def.params, def.options);
33665 if (active) active.$$addStateInfo(ref.state, def.params);
33666 if (def.href !== null) attrs.$set(type.attr, def.href);
33669 if (ref.paramExpr) {
33670 scope.$watch(ref.paramExpr, function(val) { if (val !== def.params) update(val); }, true);
33671 def.params = angular.copy(scope.$eval(ref.paramExpr));
33675 if (!type.clickable) return;
33676 element.bind("click", clickHook(element, $state, $timeout, type, function() { return def; }));
33683 * @name ui.router.state.directive:ui-state
33685 * @requires ui.router.state.uiSref
33690 * Much like ui-sref, but will accept named $scope properties to evaluate for a state definition,
33691 * params and override options.
33693 * @param {string} ui-state 'stateName' can be any valid absolute or relative state
33694 * @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#href $state.href()}
33695 * @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33697 $StateRefDynamicDirective.$inject = ['$state', '$timeout'];
33698 function $StateRefDynamicDirective($state, $timeout) {
33701 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33702 link: function(scope, element, attrs, uiSrefActive) {
33703 var type = getTypeInfo(element);
33704 var active = uiSrefActive[1] || uiSrefActive[0];
33705 var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null];
33706 var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']';
33707 var def = { state: null, params: null, options: null, href: null };
33709 function runStateRefLink (group) {
33710 def.state = group[0]; def.params = group[1]; def.options = group[2];
33711 def.href = $state.href(def.state, def.params, def.options);
33713 if (active) active.$$addStateInfo(def.state, def.params);
33714 if (def.href) attrs.$set(type.attr, def.href);
33717 scope.$watch(watch, runStateRefLink, true);
33718 runStateRefLink(scope.$eval(watch));
33720 if (!type.clickable) return;
33721 element.bind("click", clickHook(element, $state, $timeout, type, function() { return def; }));
33723 var ref = parseStateRef(attrs.uiSref, $state.current.name);
33724 var params = null, url = null, base = stateContext(element) || $state.$current;
33725 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33726 var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
33727 'xlink:href' : 'href';
33728 var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
33729 var isForm = element[0].nodeName === "FORM";
33730 var attr = isForm ? "action" : hrefKind, nav = true;
33732 var options = { relative: base, inherit: true };
33733 var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
33735 angular.forEach(allowedOptions, function(option) {
33736 if (option in optionsOverride) {
33737 options[option] = optionsOverride[option];
33741 var update = function(newVal) {
33742 if (newVal) params = angular.copy(newVal);
33745 newHref = $state.href(ref.state, params, options);
33747 var activeDirective = uiSrefActive[1] || uiSrefActive[0];
33748 if (activeDirective) {
33749 activeDirective.$$addStateInfo(ref.state, params);
33751 if (newHref === null) {
33755 attrs.$set(attr, newHref);
33758 if (ref.paramExpr) {
33759 scope.$watch(ref.paramExpr, function(newVal, oldVal) {
33760 if (newVal !== params) update(newVal);
33762 params = angular.copy(scope.$eval(ref.paramExpr));
33766 if (isForm) return;
33768 element.bind("click", function(e) {
33769 var button = e.which || e.button;
33770 if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
33771 // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33772 var transition = $timeout(function() {
33773 $state.go(ref.state, params, options);
33775 e.preventDefault();
33777 // if the state has no URL, ignore one preventDefault from the <a> directive.
33778 var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
33779 e.preventDefault = function() {
33780 if (ignorePreventDefaultCount-- <= 0)
33781 $timeout.cancel(transition);
33785 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33793 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33796 * @name ui.router.state.directive:ui-sref-active
33798 * @requires ui.router.state.$state
33799 * @requires ui.router.state.$stateParams
33800 * @requires $interpolate
33805 * A directive working alongside ui-sref to add classes to an element when the
33806 * related ui-sref directive's state is active, and removing them when it is inactive.
33807 * The primary use-case is to simplify the special appearance of navigation menus
33808 * relying on `ui-sref`, by having the "active" state's menu button appear different,
33809 * distinguishing it from the inactive menu items.
33811 * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
33812 * ui-sref-active found at the same level or above the ui-sref will be used.
33814 * Will activate when the ui-sref's target state or any child state is active. If you
33815 * need to activate only when the ui-sref target state is active and *not* any of
33816 * it's children, then you will use
33817 * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
33820 * Given the following template:
33823 * <li ui-sref-active="active" class="item">
33824 * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
33830 * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
33831 * the resulting HTML will appear as (note the 'active' class):
33834 * <li ui-sref-active="active" class="item active">
33835 * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
33840 * The class name is interpolated **once** during the directives link time (any further changes to the
33841 * interpolated value are ignored).
33843 * Multiple classes may be specified in a space-separated format:
33846 * <li ui-sref-active='class1 class2 class3'>
33847 * <a ui-sref="app.user">link</a>
33853 * It is also possible to pass ui-sref-active an expression that evaluates
33854 * to an object hash, whose keys represent active class names and whose
33855 * values represent the respective state names/globs.
33856 * ui-sref-active will match if the current active state **includes** any of
33857 * the specified state names/globs, even the abstract ones.
33860 * Given the following template, with "admin" being an abstract state:
33862 * <div ui-sref-active="{'active': 'admin.*'}">
33863 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
33867 * When the current state is "admin.roles" the "active" class will be applied
33868 * to both the <div> and <a> elements. It is important to note that the state
33869 * names/globs passed to ui-sref-active shadow the state provided by ui-sref.
33871 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33876 * @name ui.router.state.directive:ui-sref-active-eq
33878 * @requires ui.router.state.$state
33879 * @requires ui.router.state.$stateParams
33880 * @requires $interpolate
33885 * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
33886 * when the exact target state used in the `ui-sref` is active; no child states.
33889 $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
33890 function $StateRefActiveDirective($state, $stateParams, $interpolate) {
33894 controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
33895 var states = [], activeClasses = {}, activeEqClass, uiSrefActive;
33897 controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
33898 var states = [], activeClass;
33899 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
33901 // There probably isn't much point in $observing this
33902 // uiSrefActive and uiSrefActiveEq share the same directive object with some
33903 // slight difference in logic routing
33905 activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
33908 uiSrefActive = $scope.$eval($attrs.uiSrefActive);
33910 // Do nothing. uiSrefActive is not a valid expression.
33911 // Fall back to using $interpolate below
33913 uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
33914 if (isObject(uiSrefActive)) {
33915 forEach(uiSrefActive, function(stateOrName, activeClass) {
33916 if (isString(stateOrName)) {
33917 var ref = parseStateRef(stateOrName, $state.current.name);
33918 addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
33923 // Allow uiSref to communicate with uiSrefActive[Equals]
33924 this.$$addStateInfo = function (newState, newParams) {
33925 // we already got an explicit state provided by ui-sref-active, so we
33926 // shadow the one that comes from ui-sref
33927 if (isObject(uiSrefActive) && states.length > 0) {
33930 addState(newState, newParams, uiSrefActive);
33934 $scope.$on('$stateChangeSuccess', update);
33936 function addState(stateName, stateParams, activeClass) {
33937 var state = $state.get(stateName, stateContext($element));
33938 var stateHash = createStateHash(stateName, stateParams);
33941 state: state || { name: stateName },
33942 params: stateParams,
33946 activeClasses[stateHash] = activeClass;
33950 * @param {string} state
33951 * @param {Object|string} [params]
33954 function createStateHash(state, params) {
33955 if (!isString(state)) {
33956 throw new Error('state should be a string');
33958 if (isObject(params)) {
33959 return state + toJson(params);
33961 params = $scope.$eval(params);
33962 if (isObject(params)) {
33963 return state + toJson(params);
33968 // Update route state
33969 function update() {
33970 for (var i = 0; i < states.length; i++) {
33971 if (anyMatch(states[i].state, states[i].params)) {
33972 addClass($element, activeClasses[states[i].hash]);
33974 removeClass($element, activeClasses[states[i].hash]);
33977 if (exactMatch(states[i].state, states[i].params)) {
33978 addClass($element, activeEqClass);
33980 removeClass($element, activeEqClass);
33985 function addClass(el, className) { $timeout(function () { el.addClass(className); }); }
33986 function removeClass(el, className) { el.removeClass(className); }
33987 function anyMatch(state, params) { return $state.includes(state.name, params); }
33988 function exactMatch(state, params) { return $state.is(state.name, params); }
33992 activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
33994 // Allow uiSref to communicate with uiSrefActive[Equals]
33995 this.$$addStateInfo = function (newState, newParams) {
33996 var state = $state.get(newState, stateContext($element));
33999 state: state || { name: newState },
34006 $scope.$on('$stateChangeSuccess', update);
34008 // Update route state
34009 function update() {
34011 $element.addClass(activeClass);
34013 $element.removeClass(activeClass);
34017 function anyMatch() {
34018 for (var i = 0; i < states.length; i++) {
34019 if (isMatch(states[i].state, states[i].params)) {
34026 function isMatch(state, params) {
34027 if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
34028 return $state.is(state.name, params);
34030 return $state.includes(state.name, params);
34033 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34038 angular.module('ui.router.state')
34039 .directive('uiSref', $StateRefDirective)
34040 .directive('uiSrefActive', $StateRefActiveDirective)
34042 .directive('uiSrefActiveEq', $StateRefActiveDirective)
34043 .directive('uiState', $StateRefDynamicDirective);
34045 .directive('uiSrefActiveEq', $StateRefActiveDirective);
34046 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34050 * @name ui.router.state.filter:isState
34052 * @requires ui.router.state.$state
34055 * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
34057 $IsStateFilter.$inject = ['$state'];
34058 function $IsStateFilter($state) {
34060 var isFilter = function (state, params) {
34061 return $state.is(state, params);
34063 var isFilter = function (state) {
34064 return $state.is(state);
34065 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34067 isFilter.$stateful = true;
34073 * @name ui.router.state.filter:includedByState
34075 * @requires ui.router.state.$state
34078 * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
34080 $IncludedByStateFilter.$inject = ['$state'];
34081 function $IncludedByStateFilter($state) {
34083 var includesFilter = function (state, params, options) {
34084 return $state.includes(state, params, options);
34086 var includesFilter = function (state) {
34087 return $state.includes(state);
34088 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34090 includesFilter.$stateful = true;
34091 return includesFilter;
34094 angular.module('ui.router.state')
34095 .filter('isState', $IsStateFilter)
34096 .filter('includedByState', $IncludedByStateFilter);
34097 })(window, window.angular);
34101 /***/ function(module, exports, __webpack_require__) {
34103 __webpack_require__(5);
34104 module.exports = 'ngResource';
34109 /***/ function(module, exports) {
34113 * @license AngularJS v1.6.2
34114 * (c) 2010-2017 Google, Inc. http://angularjs.org
34117 (function(window, angular) {'use strict';
34119 * @license AngularJS v1.4.8
34120 * (c) 2010-2015 Google, Inc. http://angularjs.org
34123 (function(window, angular, undefined) {'use strict';
34124 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34126 var $resourceMinErr = angular.$$minErr('$resource');
34128 // Helper functions and regex to lookup a dotted path on an object
34129 // stopping at undefined/null. The path must be composed of ASCII
34130 // identifiers (just like $parse)
34131 var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
34133 function isValidDottedPath(path) {
34134 return (path != null && path !== '' && path !== 'hasOwnProperty' &&
34135 MEMBER_NAME_REGEX.test('.' + path));
34138 function lookupDottedPath(obj, path) {
34139 if (!isValidDottedPath(path)) {
34140 throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
34142 var keys = path.split('.');
34143 for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
34145 obj = (obj !== null) ? obj[key] : undefined;
34151 * Create a shallow copy of an object and clear other fields from the destination
34153 function shallowClearAndCopy(src, dst) {
34156 angular.forEach(dst, function(value, key) {
34160 for (var key in src) {
34161 if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
34162 dst[key] = src[key];
34176 * The `ngResource` module provides interaction support with RESTful services
34177 * via the $resource service.
34180 * <div doc-module-components="ngResource"></div>
34183 * See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
34188 * @name $resourceProvider
34192 * Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
34196 * Requires the {@link ngResource } module to be installed.
34199 * See {@link ngResource.$resource `$resource`} for usage.
34200 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34208 * @requires ng.$log
34210 * @requires ng.$timeout
34212 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34215 * A factory which creates a resource object that lets you interact with
34216 * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
34218 * The returned resource object has action methods which provide high-level behaviors without
34219 * the need to interact with the low level {@link ng.$http $http} service.
34221 * Requires the {@link ngResource `ngResource`} module to be installed.
34223 * By default, trailing slashes will be stripped from the calculated URLs,
34224 * which can pose problems with server backends that do not expect that
34225 * behavior. This can be disabled by configuring the `$resourceProvider` like
34229 app.config(['$resourceProvider', function($resourceProvider) {
34230 // Don't strip trailing slashes from calculated URLs
34231 $resourceProvider.defaults.stripTrailingSlashes = false;
34235 * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
34236 * `/user/:username`. If you are using a URL with a port number (e.g.
34237 * `http://example.com:8080/api`), it will be respected.
34239 * If you are using a url with a suffix, just add the suffix, like this:
34240 * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
34241 * or even `$resource('http://example.com/resource/:resource_id.:format')`
34242 * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
34243 * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
34244 * can escape it with `/\.`.
34246 * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
34248 * `actions` methods. If a parameter value is a function, it will be called every time
34249 * a param value needs to be obtained for a request (unless the param was overridden). The function
34250 * will be passed the current data value as an argument.
34252 * `actions` methods. If any of the parameter value is a function, it will be executed every time
34253 * when a param value needs to be obtained for a request (unless the param was overridden).
34254 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34256 * Each key value in the parameter object is first bound to url template if present and then any
34257 * excess keys are appended to the url search query after the `?`.
34259 * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
34260 * URL `/path/greet?salutation=Hello`.
34263 * If the parameter value is prefixed with `@`, then the value for that parameter will be
34264 * extracted from the corresponding property on the `data` object (provided when calling a
34265 * "non-GET" action method).
34266 * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
34267 * `someParam` will be `data.someProp`.
34268 * Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
34269 * method that does not accept a request body)
34271 * @param {Object.<Object>=} actions Hash with declaration of custom actions that will be available
34272 * in addition to the default set of resource actions (see below). If a custom action has the same
34273 * key as a default action (e.g. `save`), then the default action will be *overwritten*, and not
34276 * The declaration should be created in the format of {@link ng.$http#usage $http.config}:
34278 * If the parameter value is prefixed with `@` then the value for that parameter will be extracted
34279 * from the corresponding property on the `data` object (provided when calling an action method). For
34280 * example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
34281 * will be `data.someProp`.
34283 * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
34284 * the default set of resource actions. The declaration should be created in the format of {@link
34285 * ng.$http#usage $http.config}:
34286 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34288 * {action1: {method:?, params:?, isArray:?, headers:?, ...},
34289 * action2: {method:?, params:?, isArray:?, headers:?, ...},
34294 * - **`action`** – {string} – The name of action. This name becomes the name of the method on
34295 * your resource object.
34296 * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
34297 * `DELETE`, `JSONP`, etc).
34298 * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
34300 * the parameter value is a function, it will be called every time when a param value needs to
34301 * be obtained for a request (unless the param was overridden). The function will be passed the
34302 * current data value as an argument.
34304 * the parameter value is a function, it will be executed every time when a param value needs to
34305 * be obtained for a request (unless the param was overridden).
34306 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34307 * - **`url`** – {string} – action specific `url` override. The url templating is supported just
34308 * like for the resource-level urls.
34309 * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
34310 * see `returns` section.
34311 * - **`transformRequest`** –
34312 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
34313 * transform function or an array of such functions. The transform function takes the http
34314 * request body and headers and returns its transformed (typically serialized) version.
34315 * By default, transformRequest will contain one function that checks if the request data is
34317 * an object and serializes it using `angular.toJson`. To prevent this behavior, set
34318 * `transformRequest` to an empty array: `transformRequest: []`
34319 * - **`transformResponse`** –
34320 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
34321 * transform function or an array of such functions. The transform function takes the http
34322 * response body, headers and status and returns its transformed (typically deserialized)
34324 * By default, transformResponse will contain one function that checks if the response looks
34325 * like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
34326 * set `transformResponse` to an empty array: `transformResponse: []`
34328 * an object and serializes to using `angular.toJson`. To prevent this behavior, set
34329 * `transformRequest` to an empty array: `transformRequest: []`
34330 * - **`transformResponse`** –
34331 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
34332 * transform function or an array of such functions. The transform function takes the http
34333 * response body and headers and returns its transformed (typically deserialized) version.
34334 * By default, transformResponse will contain one function that checks if the response looks like
34335 * a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
34336 * `transformResponse` to an empty array: `transformResponse: []`
34337 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34338 * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
34339 * GET request, otherwise if a cache instance built with
34340 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
34343 * - **`timeout`** – `{number}` – timeout in milliseconds.<br />
34344 * **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
34345 * **not** supported in $resource, because the same value would be used for multiple requests.
34346 * If you are looking for a way to cancel requests, you should use the `cancellable` option.
34347 * - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call
34348 * will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
34349 * return value. Calling `$cancelRequest()` for a non-cancellable or an already
34350 * completed/cancelled request will have no effect.<br />
34352 * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
34353 * should abort the request when resolved.
34354 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34355 * - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
34357 * [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
34358 * for more information.
34359 * - **`responseType`** - `{string}` - see
34360 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
34361 * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
34362 * `response` and `responseError`. Both `response` and `responseError` interceptors get called
34363 * with `http response` object. See {@link ng.$http $http interceptors}.
34365 * @param {Object} options Hash with custom settings that should extend the
34367 * default `$resourceProvider` behavior. The supported options are:
34369 * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
34370 * slashes from any calculated URL will be stripped. (Defaults to true.)
34371 * - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be
34372 * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value.
34373 * This can be overwritten per action. (Defaults to false.)
34375 * default `$resourceProvider` behavior. The only supported option is
34379 * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
34380 * slashes from any calculated URL will be stripped. (Defaults to true.)
34381 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34383 * @returns {Object} A resource "class" object with methods for the default set of resource actions
34384 * optionally extended with custom `actions`. The default set contains these actions:
34386 * { 'get': {method:'GET'},
34387 * 'save': {method:'POST'},
34388 * 'query': {method:'GET', isArray:true},
34389 * 'remove': {method:'DELETE'},
34390 * 'delete': {method:'DELETE'} };
34393 * Calling these methods invoke an {@link ng.$http} with the specified http method,
34394 * destination and parameters. When the data is returned from the server then the object is an
34395 * instance of the resource class. The actions `save`, `remove` and `delete` are available on it
34396 * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
34397 * read, update, delete) on server-side data like this:
34399 * var User = $resource('/user/:userId', {userId:'@id'});
34400 * var user = User.get({userId:123}, function() {
34406 * It is important to realize that invoking a $resource object method immediately returns an
34407 * empty reference (object or array depending on `isArray`). Once the data is returned from the
34408 * server the existing reference is populated with the actual data. This is a useful trick since
34409 * usually the resource is assigned to a model which is then rendered by the view. Having an empty
34410 * object results in no rendering, once the data arrives from the server then the object is
34411 * populated with the data and the view automatically re-renders itself showing the new data. This
34412 * means that in most cases one never has to write a callback function for the action methods.
34414 * The action methods on the class object or instance object can be invoked with the following
34417 * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
34418 * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
34419 * - non-GET instance actions: `instance.$action([parameters], [success], [error])`
34423 * Success callback is called with (value (Object|Array), responseHeaders (Function),
34424 * status (number), statusText (string)) arguments, where the value is the populated resource
34425 * instance or collection object. The error callback is called with (httpResponse) argument.
34427 * Success callback is called with (value, responseHeaders) arguments, where the value is
34428 * the populated resource instance or collection object. The error callback is called
34429 * with (httpResponse) argument.
34430 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34432 * Class actions return empty instance (with additional properties below).
34433 * Instance actions return promise of the action.
34436 * The Resource instances and collections have these additional properties:
34438 * The Resource instances and collection have these additional properties:
34439 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34441 * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
34442 * instance or collection.
34444 * On success, the promise is resolved with the same resource instance or collection object,
34445 * updated with data from server. This makes it easy to use in
34446 * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
34447 * rendering until the resource(s) are loaded.
34450 * On failure, the promise is rejected with the {@link ng.$http http response} object, without
34452 * On failure, the promise is resolved with the {@link ng.$http http response} object, without
34453 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34454 * the `resource` property.
34456 * If an interceptor object was provided, the promise will instead be resolved with the value
34457 * returned by the interceptor.
34459 * - `$resolved`: `true` after first server interaction is completed (either with success or
34460 * rejection), `false` before that. Knowing if the Resource has been resolved is useful in
34464 * The Resource instances and collections have these additional methods:
34466 * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
34467 * collection, calling this method will abort the request.
34469 * The Resource instances have these additional methods:
34471 * - `toJSON`: It returns a simple object without any of the extra properties added as part of
34472 * the Resource API. This object can be serialized through {@link angular.toJson} safely
34473 * without attaching Angular-specific fields. Notice that `JSON.stringify` (and
34474 * `angular.toJson`) automatically use this method when serializing a Resource instance
34475 * (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
34478 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34481 * # Credit card resource
34484 // Define CreditCard class
34485 var CreditCard = $resource('/user/:userId/card/:cardId',
34486 {userId:123, cardId:'@id'}, {
34487 charge: {method:'POST', params:{charge:true}}
34490 // We can retrieve a collection from the server
34491 var cards = CreditCard.query(function() {
34492 // GET: /user/123/card
34493 // server returns: [ {id:456, number:'1234', name:'Smith'} ];
34495 var card = cards[0];
34496 // each item is an instance of CreditCard
34497 expect(card instanceof CreditCard).toEqual(true);
34498 card.name = "J. Smith";
34499 // non GET methods are mapped onto the instances
34501 // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
34502 // server returns: {id:456, number:'1234', name: 'J. Smith'};
34504 // our custom method is mapped as well.
34505 card.$charge({amount:9.99});
34506 // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
34509 // we can create an instance as well
34510 var newCard = new CreditCard({number:'0123'});
34511 newCard.name = "Mike Smith";
34513 // POST: /user/123/card {number:'0123', name:'Mike Smith'}
34514 // server returns: {id:789, number:'0123', name: 'Mike Smith'};
34515 expect(newCard.id).toEqual(789);
34518 * The object returned from this function execution is a resource "class" which has "static" method
34519 * for each action in the definition.
34521 * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
34530 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34531 * When the data is returned from the server then the object is an instance of the resource type and
34532 * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
34533 * operations (create, read, update, delete) on server-side data.
34536 var User = $resource('/user/:userId', {userId:'@id'});
34537 User.get({userId:123}, function(user) {
34543 * It's worth noting that the success callback for `get`, `query` and other methods gets passed
34544 * in the response that came from the server as well as $http header getter function, so one
34545 * could rewrite the above example and get access to http headers as:
34548 var User = $resource('/user/:userId', {userId:'@id'});
34550 User.get({userId:123}, function(user, getResponseHeaders){
34552 user.$save(function(user, putResponseHeaders) {
34553 //user => saved user object
34555 User.get({userId:123}, function(u, getResponseHeaders){
34557 u.$save(function(u, putResponseHeaders) {
34558 //u => saved user object
34559 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34560 //putResponseHeaders => $http header getter
34565 * You can also access the raw `$http` promise via the `$promise` property on the object returned
34568 var User = $resource('/user/:userId', {userId:'@id'});
34569 User.get({userId:123})
34570 .$promise.then(function(user) {
34571 $scope.user = user;
34578 * # Creating a custom 'PUT' request
34582 * # Creating a custom 'PUT' request
34583 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34584 * In this example we create a custom method on our resource to make a PUT request
34586 * var app = angular.module('app', ['ngResource', 'ngRoute']);
34588 * // Some APIs expect a PUT request in the format URL/object/ID
34589 * // Here we are creating an 'update' method
34590 * app.factory('Notes', ['$resource', function($resource) {
34591 * return $resource('/notes/:id', null,
34593 * 'update': { method:'PUT' }
34597 * // In our controller we get the ID from the URL using ngRoute and $routeParams
34598 * // We pass in $routeParams and our Notes factory along with $scope
34599 * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
34600 function($scope, $routeParams, Notes) {
34601 * // First get a note object from the factory
34602 * var note = Notes.get({ id:$routeParams.id });
34605 * // Now call update passing in the ID first then the object you are updating
34606 * Notes.update({ id:$id }, note);
34608 * // This will PUT /notes/ID with the note object in the request payload
34615 * # Cancelling requests
34617 * If an action's configuration specifies that it is cancellable, you can cancel the request related
34618 * to an instance or collection (as long as it is a result of a "non-instance" call):
34621 // ...defining the `Hotel` resource...
34622 var Hotel = $resource('/api/hotel/:id', {id: '@id'}, {
34623 // Let's make the `query()` method cancellable
34624 query: {method: 'get', isArray: true, cancellable: true}
34627 // ...somewhere in the PlanVacationController...
34629 this.onDestinationChanged = function onDestinationChanged(destination) {
34630 // We don't care about any pending request for hotels
34631 // in a different destination any more
34632 this.availableHotels.$cancelRequest();
34634 // Let's query for hotels in '<destination>'
34635 // (calls: /api/hotel?location=<destination>)
34636 this.availableHotels = Hotel.query({location: destination});
34641 angular.module('ngResource', ['ng']).
34642 provider('$resource', function ResourceProvider() {
34643 var PROTOCOL_AND_IPV6_REGEX = /^https?:\/\/\[[^\]]*][^/]*/;
34645 var provider = this;
34649 * @name $resourceProvider#defaults
34651 * Object containing default options used when creating `$resource` instances.
34653 * The default values satisfy a wide range of usecases, but you may choose to overwrite any of
34654 * them to further customize your instances. The available properties are:
34656 * - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
34657 * calculated URL will be stripped.<br />
34658 * (Defaults to true.)
34659 * - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
34660 * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
34661 * value. For more details, see {@link ngResource.$resource}. This can be overwritten per
34662 * resource class or action.<br />
34663 * (Defaults to false.)
34664 * - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
34665 * high-level methods corresponding to RESTful actions/methods on resources. An action may
34666 * specify what HTTP method to use, what URL to hit, if the return value will be a single
34667 * object or a collection (array) of objects etc. For more details, see
34668 * {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
34670 * The default actions are:
34673 * get: {method: 'GET'},
34674 * save: {method: 'POST'},
34675 * query: {method: 'GET', isArray: true},
34676 * remove: {method: 'DELETE'},
34677 * delete: {method: 'DELETE'}
34683 * For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
34688 * config(['$resourceProvider', function ($resourceProvider) {
34689 * $resourceProvider.defaults.actions.update = {
34695 * Or you can even overwrite the whole `actions` list and specify your own:
34700 * config(['$resourceProvider', function ($resourceProvider) {
34701 * $resourceProvider.defaults.actions = {
34702 * create: {method: 'POST'},
34703 * get: {method: 'GET'},
34704 * getAll: {method: 'GET', isArray:true},
34705 * update: {method: 'PUT'},
34706 * delete: {method: 'DELETE'}
34714 angular.module('ngResource', ['ng']).
34715 provider('$resource', function() {
34716 var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
34717 var provider = this;
34719 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34721 // Strip slashes by default
34722 stripTrailingSlashes: true,
34725 // Make non-instance requests cancellable (via `$cancelRequest()`)
34726 cancellable: false,
34729 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34730 // Default actions configuration
34732 'get': {method: 'GET'},
34733 'save': {method: 'POST'},
34734 'query': {method: 'GET', isArray: true},
34735 'remove': {method: 'DELETE'},
34736 'delete': {method: 'DELETE'}
34741 this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
34743 var noop = angular.noop,
34744 forEach = angular.forEach,
34745 extend = angular.extend,
34746 copy = angular.copy,
34747 isArray = angular.isArray,
34748 isDefined = angular.isDefined,
34749 isFunction = angular.isFunction,
34750 isNumber = angular.isNumber,
34751 encodeUriQuery = angular.$$encodeUriQuery,
34752 encodeUriSegment = angular.$$encodeUriSegment;
34754 this.$get = ['$http', '$q', function($http, $q) {
34756 var noop = angular.noop,
34757 forEach = angular.forEach,
34758 extend = angular.extend,
34759 copy = angular.copy,
34760 isFunction = angular.isFunction;
34763 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
34764 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
34765 * (pchar) allowed in path segments:
34767 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
34768 * pct-encoded = "%" HEXDIG HEXDIG
34769 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
34770 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
34771 * / "*" / "+" / "," / ";" / "="
34773 function encodeUriSegment(val) {
34774 return encodeUriQuery(val, true).
34775 replace(/%26/gi, '&').
34776 replace(/%3D/gi, '=').
34777 replace(/%2B/gi, '+');
34782 * This method is intended for encoding *key* or *value* parts of query component. We need a
34783 * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
34784 * have to be encoded per http://tools.ietf.org/html/rfc3986:
34785 * query = *( pchar / "/" / "?" )
34786 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
34787 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
34788 * pct-encoded = "%" HEXDIG HEXDIG
34789 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
34790 * / "*" / "+" / "," / ";" / "="
34792 function encodeUriQuery(val, pctEncodeSpaces) {
34793 return encodeURIComponent(val).
34794 replace(/%40/gi, '@').
34795 replace(/%3A/gi, ':').
34796 replace(/%24/g, '$').
34797 replace(/%2C/gi, ',').
34798 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
34800 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34802 function Route(template, defaults) {
34803 this.template = template;
34804 this.defaults = extend({}, provider.defaults, defaults);
34805 this.urlParams = {};
34808 Route.prototype = {
34809 setUrlParams: function(config, params, actionUrl) {
34811 url = actionUrl || self.template,
34815 protocolAndIpv6 = '';
34817 var urlParams = self.urlParams = Object.create(null);
34818 forEach(url.split(/\W/), function(param) {
34819 if (param === 'hasOwnProperty') {
34820 throw $resourceMinErr('badname', 'hasOwnProperty is not a valid parameter name.');
34822 if (!(new RegExp('^\\d+$').test(param)) && param &&
34823 (new RegExp('(^|[^\\\\]):' + param + '(\\W|$)').test(url))) {
34824 urlParams[param] = {
34825 isQueryParamValue: (new RegExp('\\?.*=:' + param + '(?:\\W|$)')).test(url)
34829 url = url.replace(/\\:/g, ':');
34830 url = url.replace(PROTOCOL_AND_IPV6_REGEX, function(match) {
34831 protocolAndIpv6 = match;
34833 protocolAndDomain = '';
34835 var urlParams = self.urlParams = {};
34836 forEach(url.split(/\W/), function(param) {
34837 if (param === 'hasOwnProperty') {
34838 throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
34840 if (!(new RegExp("^\\d+$").test(param)) && param &&
34841 (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
34842 urlParams[param] = true;
34845 url = url.replace(/\\:/g, ':');
34846 url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
34847 protocolAndDomain = match;
34848 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34852 params = params || {};
34854 forEach(self.urlParams, function(paramInfo, urlParam) {
34855 val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
34856 if (isDefined(val) && val !== null) {
34857 if (paramInfo.isQueryParamValue) {
34858 encodedVal = encodeUriQuery(val, true);
34860 encodedVal = encodeUriSegment(val);
34862 url = url.replace(new RegExp(':' + urlParam + '(\\W|$)', 'g'), function(match, p1) {
34863 return encodedVal + p1;
34866 url = url.replace(new RegExp('(/?):' + urlParam + '(\\W|$)', 'g'), function(match,
34867 leadingSlashes, tail) {
34868 if (tail.charAt(0) === '/') {
34870 forEach(self.urlParams, function(_, urlParam) {
34871 val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
34872 if (angular.isDefined(val) && val !== null) {
34873 encodedVal = encodeUriSegment(val);
34874 url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
34875 return encodedVal + p1;
34878 url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
34879 leadingSlashes, tail) {
34880 if (tail.charAt(0) == '/') {
34881 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34884 return leadingSlashes + tail;
34890 // strip trailing slashes and set the url (unless this behavior is specifically disabled)
34891 if (self.defaults.stripTrailingSlashes) {
34892 url = url.replace(/\/+$/, '') || '/';
34896 // Collapse `/.` if found in the last URL path segment before the query.
34897 // E.g. `http://url.com/id/.format?q=x` becomes `http://url.com/id.format?q=x`.
34898 url = url.replace(/\/\.(?=\w+($|\?))/, '.');
34899 // Replace escaped `/\.` with `/.`.
34900 // (If `\.` comes from a param value, it will be encoded as `%5C.`.)
34901 config.url = protocolAndIpv6 + url.replace(/\/(\\|%5C)\./, '/.');
34903 // then replace collapse `/.` if found in the last URL path segment before the query
34904 // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
34905 url = url.replace(/\/\.(?=\w+($|\?))/, '.');
34906 // replace escaped `/\.` with `/.`
34907 config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
34908 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34911 // set params - delegate param encoding to $http
34912 forEach(params, function(value, key) {
34913 if (!self.urlParams[key]) {
34914 config.params = config.params || {};
34915 config.params[key] = value;
34922 function resourceFactory(url, paramDefaults, actions, options) {
34923 var route = new Route(url, options);
34925 actions = extend({}, provider.defaults.actions, actions);
34927 function extractParams(data, actionParams) {
34929 actionParams = extend({}, paramDefaults, actionParams);
34930 forEach(actionParams, function(value, key) {
34932 if (isFunction(value)) { value = value(data); }
34933 ids[key] = value && value.charAt && value.charAt(0) === '@' ?
34935 if (isFunction(value)) { value = value(); }
34936 ids[key] = value && value.charAt && value.charAt(0) == '@' ?
34937 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34938 lookupDottedPath(data, value.substr(1)) : value;
34943 function defaultResponseInterceptor(response) {
34944 return response.resource;
34947 function Resource(value) {
34948 shallowClearAndCopy(value || {}, this);
34951 Resource.prototype.toJSON = function() {
34952 var data = extend({}, this);
34953 delete data.$promise;
34954 delete data.$resolved;
34956 delete data.$cancelRequest;
34958 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34962 forEach(actions, function(action, name) {
34963 var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
34965 var numericTimeout = action.timeout;
34966 var cancellable = isDefined(action.cancellable) ?
34967 action.cancellable : route.defaults.cancellable;
34969 if (numericTimeout && !isNumber(numericTimeout)) {
34970 $log.debug('ngResource:\n' +
34971 ' Only numeric values are allowed as `timeout`.\n' +
34972 ' Promises are not supported in $resource, because the same value would ' +
34973 'be used for multiple requests. If you are looking for a way to cancel ' +
34974 'requests, you should use the `cancellable` option.');
34975 delete action.timeout;
34976 numericTimeout = null;
34979 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34981 Resource[name] = function(a1, a2, a3, a4) {
34982 var params = {}, data, success, error;
34986 /* jshint -W086 */ /* (purposefully fall through case statements) */
34987 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34988 switch (arguments.length) {
34996 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
34999 if (isFunction(a2)) {
35000 if (isFunction(a1)) {
35012 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35022 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35024 if (isFunction(a1)) success = a1;
35025 else if (hasBody) data = a1;
35030 throw $resourceMinErr('badargs',
35032 'Expected up to 4 arguments [params, data, success, error], got {0} arguments',
35036 "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
35039 /* jshint +W086 */ /* (purposefully fall through case statements) */
35040 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35042 var isInstanceCall = this instanceof Resource;
35043 var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
35044 var httpConfig = {};
35045 var responseInterceptor = action.interceptor && action.interceptor.response ||
35046 defaultResponseInterceptor;
35047 var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
35050 var hasError = !!error;
35051 var hasResponseErrorInterceptor = !!responseErrorInterceptor;
35052 var timeoutDeferred;
35053 var numericTimeoutPromise;
35055 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35057 forEach(action, function(value, key) {
35060 httpConfig[key] = copy(value);
35064 case 'interceptor':
35066 case 'cancellable':
35070 httpConfig[key] = value;
35071 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35077 if (!isInstanceCall && cancellable) {
35078 timeoutDeferred = $q.defer();
35079 httpConfig.timeout = timeoutDeferred.promise;
35081 if (numericTimeout) {
35082 numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
35087 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35088 if (hasBody) httpConfig.data = data;
35089 route.setUrlParams(httpConfig,
35090 extend({}, extractParams(data, action.params || {}), params),
35093 var promise = $http(httpConfig).then(function(response) {
35095 var data = response.data;
35098 // Need to convert action.isArray to boolean in case it is undefined
35099 if (isArray(data) !== (!!action.isArray)) {
35100 throw $resourceMinErr('badcfg',
35101 'Error in resource configuration for action `{0}`. Expected response to ' +
35102 'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
35103 isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
35105 if (action.isArray) {
35107 forEach(data, function(item) {
35108 if (typeof item === 'object') {
35110 var data = response.data,
35111 promise = value.$promise;
35114 // Need to convert action.isArray to boolean in case it is undefined
35116 if (angular.isArray(data) !== (!!action.isArray)) {
35117 throw $resourceMinErr('badcfg',
35118 'Error in resource configuration for action `{0}`. Expected response to ' +
35119 'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
35120 angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
35123 if (action.isArray) {
35125 forEach(data, function(item) {
35126 if (typeof item === "object") {
35127 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35128 value.push(new Resource(item));
35130 // Valid JSON values may be string literals, and these should not be converted
35131 // into objects. These items will not have access to the Resource prototype
35132 // methods, but unfortunately there
35138 var promise = value.$promise; // Save the promise
35139 shallowClearAndCopy(data, value);
35140 value.$promise = promise; // Restore the promise
35143 response.resource = value;
35148 promise = promise['finally'](function() {
35149 value.$resolved = true;
35150 if (!isInstanceCall && cancellable) {
35151 value.$cancelRequest = noop;
35152 $timeout.cancel(numericTimeoutPromise);
35153 timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
35156 shallowClearAndCopy(data, value);
35157 value.$promise = promise;
35161 value.$resolved = true;
35163 response.resource = value;
35166 }, function(response) {
35167 value.$resolved = true;
35169 (error || noop)(response);
35171 return $q.reject(response);
35172 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35175 promise = promise.then(
35176 function(response) {
35177 var value = responseInterceptor(response);
35179 (success || noop)(value, response.headers, response.status, response.statusText);
35182 (hasError || hasResponseErrorInterceptor) ?
35183 function(response) {
35184 if (hasError && !hasResponseErrorInterceptor) {
35185 // Avoid `Possibly Unhandled Rejection` error,
35186 // but still fulfill the returned promise with a rejection
35187 promise.catch(noop);
35189 if (hasError) error(response);
35190 return hasResponseErrorInterceptor ?
35191 responseErrorInterceptor(response) :
35192 $q.reject(response);
35196 (success || noop)(value, response.headers);
35199 responseErrorInterceptor);
35200 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35202 if (!isInstanceCall) {
35203 // we are creating instance / collection
35204 // - set the initial promise
35205 // - return the instance / collection
35206 value.$promise = promise;
35207 value.$resolved = false;
35209 if (cancellable) value.$cancelRequest = cancelRequest;
35211 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35220 function cancelRequest(value) {
35221 promise.catch(noop);
35222 timeoutDeferred.resolve(value);
35225 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35229 Resource.prototype['$' + name] = function(params, success, error) {
35230 if (isFunction(params)) {
35231 error = success; success = params; params = {};
35233 var result = Resource[name].call(this, params, this, success, error);
35234 return result.$promise || result;
35238 Resource.bind = function(additionalParamDefaults) {
35240 var extendedParamDefaults = extend({}, paramDefaults, additionalParamDefaults);
35241 return resourceFactory(url, extendedParamDefaults, actions, options);
35243 return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
35244 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35250 return resourceFactory;
35255 })(window, window.angular);
35260 /***/ function(module, exports, __webpack_require__) {
35262 __webpack_require__(7);
35264 module.exports = 'ui.bootstrap';
35269 /***/ function(module, exports) {
35272 * angular-ui-bootstrap
35273 * http://angular-ui.github.io/bootstrap/
35276 * Version: 1.3.3 - 2016-05-22
35278 */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
35279 angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
35280 angular.module('ui.bootstrap.collapse', [])
35282 .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) {
35283 var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
35285 link: function(scope, element, attrs) {
35286 var expandingExpr = $parse(attrs.expanding),
35287 expandedExpr = $parse(attrs.expanded),
35288 collapsingExpr = $parse(attrs.collapsing),
35289 collapsedExpr = $parse(attrs.collapsed);
35291 if (!scope.$eval(attrs.uibCollapse)) {
35292 element.addClass('in')
35293 .addClass('collapse')
35294 .attr('aria-expanded', true)
35295 .attr('aria-hidden', false)
35297 * Version: 1.0.0 - 2016-01-08
35300 angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
35301 angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/popup.html","uib/template/datepicker/year.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
35302 angular.module('ui.bootstrap.collapse', [])
35304 .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
35305 var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
35307 link: function(scope, element, attrs) {
35308 if (!scope.$eval(attrs.uibCollapse)) {
35309 element.addClass('in')
35310 .addClass('collapse')
35311 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35312 .css({height: 'auto'});
35315 function expand() {
35317 if (element.hasClass('collapse') && element.hasClass('in')) {
35321 $q.resolve(expandingExpr(scope))
35323 element.removeClass('collapse')
35324 .addClass('collapsing')
35325 .attr('aria-expanded', true)
35326 .attr('aria-hidden', false);
35329 $animateCss(element, {
35332 to: { height: element[0].scrollHeight + 'px' }
35333 }).start()['finally'](expandDone);
35335 $animate.addClass(element, 'in', {
35336 to: { height: element[0].scrollHeight + 'px' }
35337 }).then(expandDone);
35341 element.removeClass('collapse')
35342 .addClass('collapsing')
35343 .attr('aria-expanded', true)
35344 .attr('aria-hidden', false);
35347 $animateCss(element, {
35350 to: { height: element[0].scrollHeight + 'px' }
35351 }).start()['finally'](expandDone);
35353 $animate.addClass(element, 'in', {
35354 to: { height: element[0].scrollHeight + 'px' }
35355 }).then(expandDone);
35357 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35360 function expandDone() {
35361 element.removeClass('collapsing')
35362 .addClass('collapse')
35363 .css({height: 'auto'});
35365 expandedExpr(scope);
35367 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35370 function collapse() {
35371 if (!element.hasClass('collapse') && !element.hasClass('in')) {
35372 return collapseDone();
35376 $q.resolve(collapsingExpr(scope))
35379 // IMPORTANT: The height must be set before adding "collapsing" class.
35380 // Otherwise, the browser attempts to animate from height 0 (in
35381 // collapsing class) to the given height here.
35382 .css({height: element[0].scrollHeight + 'px'})
35383 // initially all panel collapse have the collapse class, this removal
35384 // prevents the animation from jumping to collapsed state
35385 .removeClass('collapse')
35386 .addClass('collapsing')
35387 .attr('aria-expanded', false)
35388 .attr('aria-hidden', true);
35391 $animateCss(element, {
35394 }).start()['finally'](collapseDone);
35396 $animate.removeClass(element, 'in', {
35398 }).then(collapseDone);
35403 // IMPORTANT: The height must be set before adding "collapsing" class.
35404 // Otherwise, the browser attempts to animate from height 0 (in
35405 // collapsing class) to the given height here.
35406 .css({height: element[0].scrollHeight + 'px'})
35407 // initially all panel collapse have the collapse class, this removal
35408 // prevents the animation from jumping to collapsed state
35409 .removeClass('collapse')
35410 .addClass('collapsing')
35411 .attr('aria-expanded', false)
35412 .attr('aria-hidden', true);
35415 $animateCss(element, {
35418 }).start()['finally'](collapseDone);
35420 $animate.removeClass(element, 'in', {
35422 }).then(collapseDone);
35424 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35427 function collapseDone() {
35428 element.css({height: '0'}); // Required so that collapse works when animation is disabled
35429 element.removeClass('collapsing')
35430 .addClass('collapse');
35432 collapsedExpr(scope);
35434 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35437 scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
35438 if (shouldCollapse) {
35448 angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
35450 .constant('uibAccordionConfig', {
35454 .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
35455 // This array keeps track of the accordion groups
35458 // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
35459 this.closeOthers = function(openGroup) {
35460 var closeOthers = angular.isDefined($attrs.closeOthers) ?
35461 $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
35463 angular.forEach(this.groups, function(group) {
35464 if (group !== openGroup) {
35465 group.isOpen = false;
35471 // This is called from the accordion-group directive to add itself to the accordion
35472 this.addGroup = function(groupScope) {
35474 this.groups.push(groupScope);
35476 groupScope.$on('$destroy', function(event) {
35477 that.removeGroup(groupScope);
35481 // This is called from the accordion-group directive when to remove itself
35482 this.removeGroup = function(group) {
35483 var index = this.groups.indexOf(group);
35484 if (index !== -1) {
35485 this.groups.splice(index, 1);
35490 // The accordion directive simply sets up the directive controller
35491 // and adds an accordion CSS class to itself element.
35492 .directive('uibAccordion', function() {
35494 controller: 'UibAccordionController',
35495 controllerAs: 'accordion',
35497 templateUrl: function(element, attrs) {
35498 return attrs.templateUrl || 'uib/template/accordion/accordion.html';
35503 // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
35504 .directive('uibAccordionGroup', function() {
35506 require: '^uibAccordion', // We need this directive to be inside an accordion
35507 transclude: true, // It transcludes the contents of the directive into the template
35508 replace: true, // The element containing the directive will be replaced with the template
35509 templateUrl: function(element, attrs) {
35510 return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
35513 heading: '@', // Interpolate the heading attribute onto this scope
35515 panelClass: '@?', // Ditto with panelClass
35517 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35521 controller: function() {
35522 this.setHeading = function(element) {
35523 this.heading = element;
35526 link: function(scope, element, attrs, accordionCtrl) {
35527 accordionCtrl.addGroup(scope);
35529 scope.openClass = attrs.openClass || 'panel-open';
35530 scope.panelClass = attrs.panelClass || 'panel-default';
35531 scope.$watch('isOpen', function(value) {
35532 element.toggleClass(scope.openClass, !!value);
35534 accordionCtrl.closeOthers(scope);
35538 scope.toggleOpen = function($event) {
35539 if (!scope.isDisabled) {
35540 if (!$event || $event.which === 32) {
35541 scope.isOpen = !scope.isOpen;
35547 var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
35548 scope.headingId = id + '-tab';
35549 scope.panelId = id + '-panel';
35551 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35556 // Use accordion-heading below an accordion-group to provide a heading containing HTML
35557 .directive('uibAccordionHeading', function() {
35559 transclude: true, // Grab the contents to be used as the heading
35560 template: '', // In effect remove this element!
35562 require: '^uibAccordionGroup',
35563 link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
35564 // Pass the heading to the accordion-group controller
35565 // so that it can be transcluded into the right place in the template
35566 // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
35567 accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
35572 // Use in the accordion-group template to indicate where you want the heading to be transcluded
35573 // You must provide the property on the accordion-group controller that will hold the transcluded element
35574 .directive('uibAccordionTransclude', function() {
35576 require: '^uibAccordionGroup',
35577 link: function(scope, element, attrs, controller) {
35578 scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
35581 var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
35583 elem.append(heading);
35585 element.find('span').html('');
35586 element.find('span').append(heading);
35587 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35594 function getHeaderSelectors() {
35595 return 'uib-accordion-header,' +
35596 'data-uib-accordion-header,' +
35597 'x-uib-accordion-header,' +
35598 'uib\\:accordion-header,' +
35599 '[uib-accordion-header],' +
35600 '[data-uib-accordion-header],' +
35601 '[x-uib-accordion-header]';
35604 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35607 angular.module('ui.bootstrap.alert', [])
35609 .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
35610 $scope.closeable = !!$attrs.close;
35612 var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
35613 $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
35615 if (dismissOnTimeout) {
35616 $timeout(function() {
35618 }, parseInt(dismissOnTimeout, 10));
35622 .directive('uibAlert', function() {
35624 controller: 'UibAlertController',
35625 controllerAs: 'alert',
35626 templateUrl: function(element, attrs) {
35627 return attrs.templateUrl || 'uib/template/alert/alert.html';
35638 angular.module('ui.bootstrap.buttons', [])
35640 .constant('uibButtonConfig', {
35641 activeClass: 'active',
35642 toggleEvent: 'click'
35645 .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
35646 this.activeClass = buttonConfig.activeClass || 'active';
35647 this.toggleEvent = buttonConfig.toggleEvent || 'click';
35650 .directive('uibBtnRadio', ['$parse', function($parse) {
35652 require: ['uibBtnRadio', 'ngModel'],
35653 controller: 'UibButtonsController',
35654 controllerAs: 'buttons',
35655 link: function(scope, element, attrs, ctrls) {
35656 var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
35657 var uncheckableExpr = $parse(attrs.uibUncheckable);
35659 element.find('input').css({display: 'none'});
35662 ngModelCtrl.$render = function() {
35663 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
35667 element.on(buttonsCtrl.toggleEvent, function() {
35668 if (attrs.disabled) {
35672 var isActive = element.hasClass(buttonsCtrl.activeClass);
35674 if (!isActive || angular.isDefined(attrs.uncheckable)) {
35675 scope.$apply(function() {
35676 ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
35677 ngModelCtrl.$render();
35682 if (attrs.uibUncheckable) {
35683 scope.$watch(uncheckableExpr, function(uncheckable) {
35685 attrs.$set('uncheckable', uncheckable ? '' : undefined);
35687 attrs.$set('uncheckable', uncheckable ? '' : null);
35688 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35695 .directive('uibBtnCheckbox', function() {
35697 require: ['uibBtnCheckbox', 'ngModel'],
35698 controller: 'UibButtonsController',
35699 controllerAs: 'button',
35700 link: function(scope, element, attrs, ctrls) {
35701 var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
35703 element.find('input').css({display: 'none'});
35705 function getTrueValue() {
35706 return getCheckboxValue(attrs.btnCheckboxTrue, true);
35709 function getFalseValue() {
35710 return getCheckboxValue(attrs.btnCheckboxFalse, false);
35713 function getCheckboxValue(attribute, defaultValue) {
35714 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
35718 ngModelCtrl.$render = function() {
35719 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
35723 element.on(buttonsCtrl.toggleEvent, function() {
35724 if (attrs.disabled) {
35728 scope.$apply(function() {
35729 ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
35730 ngModelCtrl.$render();
35737 angular.module('ui.bootstrap.carousel', [])
35739 .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
35741 slides = self.slides = $scope.slides = [],
35742 SLIDE_DIRECTION = 'uib-slideDirection',
35744 currentIndex = $scope.active,
35745 currentInterval, isPlaying, bufferedTransitions = [];
35748 currentInterval, isPlaying, bufferedTransitions = [];
35749 self.currentSlide = null;
35750 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35752 var destroyed = false;
35754 self.addSlide = function(slide, element) {
35760 slides.sort(function(a, b) {
35761 return +a.slide.index - +b.slide.index;
35763 //if this is the first slide or the slide is set to active, select it
35764 if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) {
35766 slide.$element = element;
35767 slides.push(slide);
35768 //if this is the first slide or the slide is set to active, select it
35769 if (slides.length === 1 || slide.active) {
35770 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35771 if ($scope.$currentTransition) {
35772 $scope.$currentTransition = null;
35776 currentIndex = slide.index;
35777 $scope.active = slide.index;
35778 setActive(currentIndex);
35779 self.select(slides[findSlideIndex(slide)]);
35780 if (slides.length === 1) {
35784 self.select(slides[slides.length - 1]);
35785 if (slides.length === 1) {
35789 slide.active = false;
35790 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35794 self.getCurrentIndex = function() {
35796 for (var i = 0; i < slides.length; i++) {
35797 if (slides[i].slide.index === currentIndex) {
35802 if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
35803 return +self.currentSlide.index;
35805 return currentIndex;
35806 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35809 self.next = $scope.next = function() {
35810 var newIndex = (self.getCurrentIndex() + 1) % slides.length;
35812 if (newIndex === 0 && $scope.noWrap()) {
35818 return self.select(slides[newIndex], 'next');
35820 return self.select(getSlideByIndex(newIndex), 'next');
35821 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35824 self.prev = $scope.prev = function() {
35825 var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
35827 if ($scope.noWrap() && newIndex === slides.length - 1) {
35833 return self.select(slides[newIndex], 'prev');
35836 self.removeSlide = function(slide) {
35837 var index = findSlideIndex(slide);
35839 var bufferedIndex = bufferedTransitions.indexOf(slides[index]);
35840 if (bufferedIndex !== -1) {
35841 bufferedTransitions.splice(bufferedIndex, 1);
35844 //get the index of the slide inside the carousel
35845 slides.splice(index, 1);
35846 if (slides.length > 0 && currentIndex === index) {
35847 if (index >= slides.length) {
35848 currentIndex = slides.length - 1;
35849 $scope.active = currentIndex;
35850 setActive(currentIndex);
35851 self.select(slides[slides.length - 1]);
35853 currentIndex = index;
35854 $scope.active = currentIndex;
35855 setActive(currentIndex);
35856 self.select(slides[index]);
35858 } else if (currentIndex > index) {
35860 $scope.active = currentIndex;
35863 //clean the active value when no more slide
35864 if (slides.length === 0) {
35865 currentIndex = null;
35866 $scope.active = null;
35868 return self.select(getSlideByIndex(newIndex), 'prev');
35871 self.removeSlide = function(slide) {
35872 if (angular.isDefined(slide.index)) {
35873 slides.sort(function(a, b) {
35874 return +a.index > +b.index;
35878 var bufferedIndex = bufferedTransitions.indexOf(slide);
35879 if (bufferedIndex !== -1) {
35880 bufferedTransitions.splice(bufferedIndex, 1);
35882 //get the index of the slide inside the carousel
35883 var index = slides.indexOf(slide);
35884 slides.splice(index, 1);
35885 $timeout(function() {
35886 if (slides.length > 0 && slide.active) {
35887 if (index >= slides.length) {
35888 self.select(slides[index - 1]);
35890 self.select(slides[index]);
35892 } else if (currentIndex > index) {
35897 //clean the currentSlide when no more slide
35898 if (slides.length === 0) {
35899 self.currentSlide = null;
35900 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35901 clearBufferedTransitions();
35905 /* direction: "prev" or "next" */
35906 self.select = $scope.select = function(nextSlide, direction) {
35908 var nextIndex = findSlideIndex(nextSlide.slide);
35910 var nextIndex = $scope.indexOfSlide(nextSlide);
35911 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35912 //Decide direction if it's not given
35913 if (direction === undefined) {
35914 direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
35916 //Prevent this user-triggered transition from occurring if there is already one in progress
35918 if (nextSlide.slide.index !== currentIndex &&
35919 !$scope.$currentTransition) {
35920 goNext(nextSlide.slide, nextIndex, direction);
35921 } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) {
35922 bufferedTransitions.push(slides[nextIndex]);
35924 if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
35925 goNext(nextSlide, nextIndex, direction);
35926 } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
35927 bufferedTransitions.push(nextSlide);
35928 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35932 /* Allow outside people to call indexOf on slides array */
35933 $scope.indexOfSlide = function(slide) {
35935 return +slide.slide.index;
35938 $scope.isActive = function(slide) {
35939 return $scope.active === slide.slide.index;
35942 $scope.isPrevDisabled = function() {
35943 return $scope.active === 0 && $scope.noWrap();
35946 $scope.isNextDisabled = function() {
35947 return $scope.active === slides.length - 1 && $scope.noWrap();
35949 return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
35952 $scope.isActive = function(slide) {
35953 return self.currentSlide === slide;
35954 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
35957 $scope.pause = function() {
35958 if (!$scope.noPause) {
35964 $scope.play = function() {
35971 $scope.$on('$destroy', function() {
35976 $scope.$watch('noTransition', function(noTransition) {
35977 $animate.enabled($element, !noTransition);
35980 $scope.$watch('interval', restartTimer);
35982 $scope.$watchCollection('slides', resetTransition);
35985 $scope.$watch('active', function(index) {
35986 if (angular.isNumber(index) && currentIndex !== index) {
35987 for (var i = 0; i < slides.length; i++) {
35988 if (slides[i].slide.index === index) {
35994 var slide = slides[index];
35997 self.select(slides[index]);
35998 currentIndex = index;
36004 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36005 function clearBufferedTransitions() {
36006 while (bufferedTransitions.length) {
36007 bufferedTransitions.shift();
36011 function getSlideByIndex(index) {
36014 if (angular.isUndefined(slides[index].index)) {
36015 return slides[index];
36017 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36018 for (var i = 0, l = slides.length; i < l; ++i) {
36019 if (slides[i].index === index) {
36026 function setActive(index) {
36027 for (var i = 0; i < slides.length; i++) {
36028 slides[i].slide.active = i === index;
36032 function goNext(slide, index, direction) {
36037 angular.extend(slide, {direction: direction});
36038 angular.extend(slides[currentIndex].slide || {}, {direction: direction});
36039 if ($animate.enabled($element) && !$scope.$currentTransition &&
36040 slides[index].element && self.slides.length > 1) {
36041 slides[index].element.data(SLIDE_DIRECTION, slide.direction);
36042 var currentIdx = self.getCurrentIndex();
36044 if (angular.isNumber(currentIdx) && slides[currentIdx].element) {
36045 slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction);
36048 $scope.$currentTransition = true;
36049 $animate.on('addClass', slides[index].element, function(element, phase) {
36051 function goNext(slide, index, direction) {
36052 if (destroyed) { return; }
36054 angular.extend(slide, {direction: direction, active: true});
36055 angular.extend(self.currentSlide || {}, {direction: direction, active: false});
36056 if ($animate.enabled($element) && !$scope.$currentTransition &&
36057 slide.$element && self.slides.length > 1) {
36058 slide.$element.data(SLIDE_DIRECTION, slide.direction);
36059 if (self.currentSlide && self.currentSlide.$element) {
36060 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
36063 $scope.$currentTransition = true;
36064 $animate.on('addClass', slide.$element, function(element, phase) {
36065 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36066 if (phase === 'close') {
36067 $scope.$currentTransition = null;
36068 $animate.off('addClass', element);
36069 if (bufferedTransitions.length) {
36071 var nextSlide = bufferedTransitions.pop().slide;
36072 var nextIndex = nextSlide.index;
36074 var nextSlide = bufferedTransitions.pop();
36075 var nextIndex = $scope.indexOfSlide(nextSlide);
36076 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36077 var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
36078 clearBufferedTransitions();
36080 goNext(nextSlide, nextIndex, nextDirection);
36087 $scope.active = slide.index;
36088 currentIndex = slide.index;
36091 self.currentSlide = slide;
36092 currentIndex = index;
36093 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36095 //every time you change slides, reset the timer
36100 function findSlideIndex(slide) {
36101 for (var i = 0; i < slides.length; i++) {
36102 if (slides[i].slide === slide) {
36109 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36110 function resetTimer() {
36111 if (currentInterval) {
36112 $interval.cancel(currentInterval);
36113 currentInterval = null;
36117 function resetTransition(slides) {
36118 if (!slides.length) {
36119 $scope.$currentTransition = null;
36120 clearBufferedTransitions();
36124 function restartTimer() {
36126 var interval = +$scope.interval;
36127 if (!isNaN(interval) && interval > 0) {
36128 currentInterval = $interval(timerFn, interval);
36132 function timerFn() {
36133 var interval = +$scope.interval;
36134 if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
36142 .directive('uibCarousel', function() {
36146 controller: 'UibCarouselController',
36147 controllerAs: 'carousel',
36148 templateUrl: function(element, attrs) {
36149 return attrs.templateUrl || 'uib/template/carousel/carousel.html';
36155 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36164 .directive('uibSlide', function() {
36166 require: '^uibCarousel',
36169 templateUrl: function(element, attrs) {
36170 return attrs.templateUrl || 'uib/template/carousel/slide.html';
36176 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36180 link: function (scope, element, attrs, carouselCtrl) {
36181 carouselCtrl.addSlide(scope, element);
36182 //when the scope is destroyed then remove the slide from the current slides array
36183 scope.$on('$destroy', function() {
36184 carouselCtrl.removeSlide(scope);
36189 scope.$watch('active', function(active) {
36191 carouselCtrl.select(scope);
36194 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36199 .animation('.item', ['$animateCss',
36200 function($animateCss) {
36201 var SLIDE_DIRECTION = 'uib-slideDirection';
36203 function removeClass(element, className, callback) {
36204 element.removeClass(className);
36211 beforeAddClass: function(element, className, done) {
36212 if (className === 'active') {
36213 var stopped = false;
36214 var direction = element.data(SLIDE_DIRECTION);
36215 var directionClass = direction === 'next' ? 'left' : 'right';
36216 var removeClassFn = removeClass.bind(this, element,
36217 directionClass + ' ' + direction, done);
36218 element.addClass(direction);
36220 $animateCss(element, {addClass: directionClass})
36222 .done(removeClassFn);
36224 return function() {
36230 beforeRemoveClass: function (element, className, done) {
36231 if (className === 'active') {
36232 var stopped = false;
36233 var direction = element.data(SLIDE_DIRECTION);
36234 var directionClass = direction === 'next' ? 'left' : 'right';
36235 var removeClassFn = removeClass.bind(this, element, directionClass, done);
36237 $animateCss(element, {addClass: directionClass})
36239 .done(removeClassFn);
36241 return function() {
36250 angular.module('ui.bootstrap.dateparser', [])
36253 .service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
36255 .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
36256 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36257 // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
36258 var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
36261 var formatCodeToRegex;
36263 this.init = function() {
36264 localeId = $locale.id;
36268 this.formatters = {};
36270 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36272 formatCodeToRegex = [
36277 apply: function(value) { this.year = +value; },
36278 formatter: function(date) {
36279 var _date = new Date();
36280 _date.setFullYear(Math.abs(date.getFullYear()));
36281 return dateFilter(_date, 'yyyy');
36284 apply: function(value) { this.year = +value; }
36285 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36291 apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
36292 formatter: function(date) {
36293 var _date = new Date();
36294 _date.setFullYear(Math.abs(date.getFullYear()));
36295 return dateFilter(_date, 'yy');
36298 apply: function(value) { this.year = +value + 2000; }
36299 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36305 apply: function(value) { this.year = +value; },
36306 formatter: function(date) {
36307 var _date = new Date();
36308 _date.setFullYear(Math.abs(date.getFullYear()));
36309 return dateFilter(_date, 'y');
36312 apply: function(value) { this.year = +value; }
36313 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36317 regex: '0?[1-9]|1[0-2]',
36319 apply: function(value) { this.month = value - 1; },
36320 formatter: function(date) {
36321 var value = date.getMonth();
36322 if (/^[0-9]$/.test(value)) {
36323 return dateFilter(date, 'MM');
36326 return dateFilter(date, 'M');
36329 apply: function(value) { this.month = value - 1; }
36330 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36334 regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
36336 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
36337 formatter: function(date) { return dateFilter(date, 'MMMM'); }
36339 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
36340 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36344 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
36346 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
36347 formatter: function(date) { return dateFilter(date, 'MMM'); }
36349 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
36350 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36354 regex: '0[1-9]|1[0-2]',
36356 apply: function(value) { this.month = value - 1; },
36357 formatter: function(date) { return dateFilter(date, 'MM'); }
36359 apply: function(value) { this.month = value - 1; }
36360 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36364 regex: '[1-9]|1[0-2]',
36366 apply: function(value) { this.month = value - 1; },
36367 formatter: function(date) { return dateFilter(date, 'M'); }
36369 apply: function(value) { this.month = value - 1; }
36370 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36374 regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
36376 apply: function(value) { this.date = +value; },
36377 formatter: function(date) {
36378 var value = date.getDate();
36379 if (/^[1-9]$/.test(value)) {
36380 return dateFilter(date, 'dd');
36383 return dateFilter(date, 'd');
36386 apply: function(value) { this.date = +value; }
36387 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36391 regex: '[0-2][0-9]{1}|3[0-1]{1}',
36393 apply: function(value) { this.date = +value; },
36394 formatter: function(date) { return dateFilter(date, 'dd'); }
36396 apply: function(value) { this.date = +value; }
36397 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36401 regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
36403 apply: function(value) { this.date = +value; },
36404 formatter: function(date) { return dateFilter(date, 'd'); }
36408 regex: $locale.DATETIME_FORMATS.DAY.join('|'),
36409 formatter: function(date) { return dateFilter(date, 'EEEE'); }
36413 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
36414 formatter: function(date) { return dateFilter(date, 'EEE'); }
36416 apply: function(value) { this.date = +value; }
36420 regex: $locale.DATETIME_FORMATS.DAY.join('|')
36424 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
36425 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36429 regex: '(?:0|1)[0-9]|2[0-3]',
36431 apply: function(value) { this.hours = +value; },
36432 formatter: function(date) { return dateFilter(date, 'HH'); }
36434 apply: function(value) { this.hours = +value; }
36435 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36439 regex: '0[0-9]|1[0-2]',
36441 apply: function(value) { this.hours = +value; },
36442 formatter: function(date) { return dateFilter(date, 'hh'); }
36444 apply: function(value) { this.hours = +value; }
36445 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36449 regex: '1?[0-9]|2[0-3]',
36451 apply: function(value) { this.hours = +value; },
36452 formatter: function(date) { return dateFilter(date, 'H'); }
36454 apply: function(value) { this.hours = +value; }
36455 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36459 regex: '[0-9]|1[0-2]',
36461 apply: function(value) { this.hours = +value; },
36462 formatter: function(date) { return dateFilter(date, 'h'); }
36464 apply: function(value) { this.hours = +value; }
36465 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36469 regex: '[0-5][0-9]',
36471 apply: function(value) { this.minutes = +value; },
36472 formatter: function(date) { return dateFilter(date, 'mm'); }
36474 apply: function(value) { this.minutes = +value; }
36475 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36479 regex: '[0-9]|[1-5][0-9]',
36481 apply: function(value) { this.minutes = +value; },
36482 formatter: function(date) { return dateFilter(date, 'm'); }
36484 apply: function(value) { this.minutes = +value; }
36485 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36489 regex: '[0-9][0-9][0-9]',
36491 apply: function(value) { this.milliseconds = +value; },
36492 formatter: function(date) { return dateFilter(date, 'sss'); }
36494 apply: function(value) { this.milliseconds = +value; }
36495 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36499 regex: '[0-5][0-9]',
36501 apply: function(value) { this.seconds = +value; },
36502 formatter: function(date) { return dateFilter(date, 'ss'); }
36504 apply: function(value) { this.seconds = +value; }
36505 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36509 regex: '[0-9]|[1-5][0-9]',
36511 apply: function(value) { this.seconds = +value; },
36512 formatter: function(date) { return dateFilter(date, 's'); }
36514 apply: function(value) { this.seconds = +value; }
36515 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36519 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
36520 apply: function(value) {
36521 if (this.hours === 12) {
36525 if (value === 'PM') {
36530 formatter: function(date) { return dateFilter(date, 'a'); }
36533 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36537 regex: '[+-]\\d{4}',
36538 apply: function(value) {
36539 var matches = value.match(/([+-])(\d{2})(\d{2})/),
36541 hours = matches[2],
36542 minutes = matches[3];
36543 this.hours += toInt(sign + hours);
36544 this.minutes += toInt(sign + minutes);
36547 formatter: function(date) {
36548 return dateFilter(date, 'Z');
36550 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36556 regex: '[0-4][0-9]|5[0-3]',
36557 formatter: function(date) { return dateFilter(date, 'ww'); }
36561 regex: '[0-9]|[1-4][0-9]|5[0-3]',
36562 formatter: function(date) { return dateFilter(date, 'w'); }
36566 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
36567 formatter: function(date) { return dateFilter(date, 'GGGG'); }
36571 regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
36572 formatter: function(date) { return dateFilter(date, 'GGG'); }
36576 regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
36577 formatter: function(date) { return dateFilter(date, 'GG'); }
36581 regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
36582 formatter: function(date) { return dateFilter(date, 'G'); }
36584 regex: '[0-4][0-9]|5[0-3]'
36588 regex: '[0-9]|[1-4][0-9]|5[0-3]'
36592 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s')
36596 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
36600 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
36604 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
36605 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36613 function createParser(format, func) {
36615 function createParser(format) {
36616 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36617 var map = [], regex = format.split('');
36619 // check for literal values
36620 var quoteIndex = format.indexOf('\'');
36621 if (quoteIndex > -1) {
36622 var inLiteral = false;
36623 format = format.split('');
36624 for (var i = quoteIndex; i < format.length; i++) {
36626 if (format[i] === '\'') {
36627 if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
36630 } else { // end of literal
36637 if (format[i] === '\'') { // start of literal
36645 format = format.join('');
36648 angular.forEach(formatCodeToRegex, function(data) {
36649 var index = format.indexOf(data.key);
36652 format = format.split('');
36654 regex[index] = '(' + data.regex + ')';
36655 format[index] = '$'; // Custom symbol to define consumed part of format
36656 for (var i = index + 1, n = index + data.key.length; i < n; i++) {
36660 format = format.join('');
36669 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36670 matcher: data.regex
36676 regex: new RegExp('^' + regex.join('') + '$'),
36677 map: orderByFilter(map, 'index')
36682 this.filter = function(date, format) {
36683 if (!angular.isDate(date) || isNaN(date) || !format) {
36687 format = $locale.DATETIME_FORMATS[format] || format;
36689 if ($locale.id !== localeId) {
36693 if (!this.formatters[format]) {
36694 this.formatters[format] = createParser(format, 'formatter');
36697 var parser = this.formatters[format],
36700 var _format = format;
36702 return map.reduce(function(str, mapper, i) {
36703 var match = _format.match(new RegExp('(.*)' + mapper.key));
36704 if (match && angular.isString(match[1])) {
36706 _format = _format.replace(match[1] + mapper.key, '');
36709 var endStr = i === map.length - 1 ? _format : '';
36711 if (mapper.apply) {
36712 return str + mapper.apply.call(null, date) + endStr;
36715 return str + endStr;
36720 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36721 this.parse = function(input, format, baseDate) {
36722 if (!angular.isString(input) || !format) {
36726 format = $locale.DATETIME_FORMATS[format] || format;
36727 format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
36729 if ($locale.id !== localeId) {
36733 if (!this.parsers[format]) {
36735 this.parsers[format] = createParser(format, 'apply');
36737 this.parsers[format] = createParser(format);
36738 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36741 var parser = this.parsers[format],
36742 regex = parser.regex,
36744 results = input.match(regex),
36746 if (results && results.length) {
36748 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
36750 year: baseDate.getFullYear(),
36751 month: baseDate.getMonth(),
36752 date: baseDate.getDate(),
36753 hours: baseDate.getHours(),
36754 minutes: baseDate.getMinutes(),
36755 seconds: baseDate.getSeconds(),
36756 milliseconds: baseDate.getMilliseconds()
36760 $log.warn('dateparser:', 'baseDate is not a valid date');
36762 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
36765 for (var i = 1, n = results.length; i < n; i++) {
36766 var mapper = map[i - 1];
36767 if (mapper.matcher === 'Z') {
36771 if (mapper.apply) {
36772 mapper.apply.call(fields, results[i]);
36776 var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
36777 Date.prototype.setFullYear;
36778 var timesetter = tzOffset ? Date.prototype.setUTCHours :
36779 Date.prototype.setHours;
36781 if (isValid(fields.year, fields.month, fields.date)) {
36782 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
36783 dt = new Date(baseDate);
36784 datesetter.call(dt, fields.year, fields.month, fields.date);
36785 timesetter.call(dt, fields.hours, fields.minutes,
36786 fields.seconds, fields.milliseconds);
36789 datesetter.call(dt, fields.year, fields.month, fields.date);
36790 timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
36791 fields.seconds || 0, fields.milliseconds || 0);
36799 // Check if date is valid for specific month (and year for February).
36800 // Month: 0 = Jan, 1 = Feb, etc
36801 function isValid(year, month, date) {
36806 if (month === 1 && date > 28) {
36807 return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
36810 if (month === 3 || month === 5 || month === 8 || month === 10) {
36817 function toInt(str) {
36818 return parseInt(str, 10);
36821 this.toTimezone = toTimezone;
36822 this.fromTimezone = fromTimezone;
36823 this.timezoneToOffset = timezoneToOffset;
36824 this.addDateMinutes = addDateMinutes;
36825 this.convertTimezoneToLocal = convertTimezoneToLocal;
36830 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36831 function toTimezone(date, timezone) {
36832 return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
36835 function fromTimezone(date, timezone) {
36836 return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
36840 //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207
36841 function timezoneToOffset(timezone, fallback) {
36842 timezone = timezone.replace(/:/g, '');
36844 //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
36845 function timezoneToOffset(timezone, fallback) {
36846 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36847 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
36848 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
36851 function addDateMinutes(date, minutes) {
36852 date = new Date(date.getTime());
36853 date.setMinutes(date.getMinutes() + minutes);
36857 function convertTimezoneToLocal(date, timezone, reverse) {
36858 reverse = reverse ? -1 : 1;
36860 var dateTimezoneOffset = date.getTimezoneOffset();
36861 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
36862 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
36864 var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
36865 return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
36866 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36870 // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
36871 // at most one element.
36872 angular.module('ui.bootstrap.isClass', [])
36873 .directive('uibIsClass', [
36875 function ($animate) {
36876 // 11111111 22222222
36877 var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
36878 // 11111111 22222222
36879 var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
36881 var dataPerTracked = {};
36886 compile: function(tElement, tAttrs) {
36888 compile: function (tElement, tAttrs) {
36889 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36890 var linkedScopes = [];
36891 var instances = [];
36892 var expToData = {};
36893 var lastActivated = null;
36894 var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
36895 var onExp = onExpMatches[2];
36896 var expsStr = onExpMatches[1];
36897 var exps = expsStr.split(',');
36901 function linkFn(scope, element, attrs) {
36902 linkedScopes.push(scope);
36909 exps.forEach(function(exp, k) {
36911 exps.forEach(function (exp, k) {
36912 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36913 addForExp(exp, scope);
36916 scope.$on('$destroy', removeScope);
36919 function addForExp(exp, scope) {
36920 var matches = exp.match(IS_REGEXP);
36921 var clazz = scope.$eval(matches[1]);
36922 var compareWithExp = matches[2];
36923 var data = expToData[exp];
36926 var watchFn = function(compareWithVal) {
36927 var newActivated = null;
36928 instances.some(function(instance) {
36930 var watchFn = function (compareWithVal) {
36931 var newActivated = null;
36932 instances.some(function (instance) {
36933 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36934 var thisVal = instance.scope.$eval(onExp);
36935 if (thisVal === compareWithVal) {
36936 newActivated = instance;
36940 if (data.lastActivated !== newActivated) {
36941 if (data.lastActivated) {
36942 $animate.removeClass(data.lastActivated.element, clazz);
36944 if (newActivated) {
36945 $animate.addClass(newActivated.element, clazz);
36947 data.lastActivated = newActivated;
36950 expToData[exp] = data = {
36951 lastActivated: null,
36954 compareWithExp: compareWithExp,
36955 watcher: scope.$watch(compareWithExp, watchFn)
36958 data.watchFn(scope.$eval(compareWithExp));
36961 function removeScope(e) {
36962 var removedScope = e.targetScope;
36963 var index = linkedScopes.indexOf(removedScope);
36964 linkedScopes.splice(index, 1);
36965 instances.splice(index, 1);
36966 if (linkedScopes.length) {
36967 var newWatchScope = linkedScopes[0];
36969 angular.forEach(expToData, function(data) {
36971 angular.forEach(expToData, function (data) {
36972 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36973 if (data.scope === removedScope) {
36974 data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
36975 data.scope = newWatchScope;
36983 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
36991 angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass'])
36993 .value('$datepickerSuppressError', false)
36995 .value('$datepickerLiteralWarning', true)
36997 .constant('uibDatepickerConfig', {
36998 datepickerMode: 'day',
37000 formatMonth: 'MMMM',
37001 formatYear: 'yyyy',
37002 formatDayHeader: 'EEE',
37003 formatDayTitle: 'MMMM yyyy',
37004 formatMonthTitle: 'yyyy',
37009 ngModelOptions: {},
37010 shortcutPropagation: false,
37016 .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
37017 function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
37019 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
37020 ngModelOptions = {},
37021 watchListeners = [],
37022 optionsUsed = !!$attrs.datepickerOptions;
37024 if (!$scope.datepickerOptions) {
37025 $scope.datepickerOptions = {};
37029 this.modes = ['day', 'month', 'year'];
37039 'formatMonthTitle',
37046 'shortcutPropagation',
37050 ].forEach(function(key) {
37052 case 'customClass':
37053 case 'dateDisabled':
37054 $scope[key] = $scope.datepickerOptions[key] || angular.noop;
37056 case 'datepickerMode':
37057 $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ?
37058 $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode;
37061 case 'formatDayHeader':
37062 case 'formatDayTitle':
37063 case 'formatMonth':
37064 case 'formatMonthTitle':
37066 self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
37067 $interpolate($scope.datepickerOptions[key])($scope.$parent) :
37068 datepickerConfig[key];
37071 case 'shortcutPropagation':
37072 case 'yearColumns':
37074 self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
37075 $scope.datepickerOptions[key] : datepickerConfig[key];
37077 case 'startingDay':
37078 if (angular.isDefined($scope.datepickerOptions.startingDay)) {
37079 self.startingDay = $scope.datepickerOptions.startingDay;
37080 } else if (angular.isNumber(datepickerConfig.startingDay)) {
37081 self.startingDay = datepickerConfig.startingDay;
37083 self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7;
37089 $scope.$watch('datepickerOptions.' + key, function(value) {
37091 if (angular.isDate(value)) {
37092 self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
37094 if ($datepickerLiteralWarning) {
37095 $log.warn('Literal date support has been deprecated, please switch to date object usage');
37098 self[key] = new Date(dateFilter(value, 'medium'));
37101 self[key] = datepickerConfig[key] ?
37102 dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) :
37106 self.refreshView();
37112 if ($scope.datepickerOptions[key]) {
37113 $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
37114 self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
37115 if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
37116 key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
37117 $scope.datepickerMode = self[key];
37118 $scope.datepickerOptions.datepickerMode = self[key];
37122 self[key] = $scope[key] = datepickerConfig[key] || null;
37129 $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
37131 $scope.disabled = angular.isDefined($attrs.disabled) || false;
37132 if (angular.isDefined($attrs.ngDisabled)) {
37133 watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
37134 $scope.disabled = disabled;
37135 self.refreshView();
37139 $scope.isActive = function(dateObject) {
37140 if (self.compare(dateObject.date, self.activeDate) === 0) {
37141 $scope.activeDateId = dateObject.uid;
37147 this.init = function(ngModelCtrl_) {
37148 ngModelCtrl = ngModelCtrl_;
37149 ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
37150 if ($scope.datepickerOptions.initDate) {
37151 self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
37152 $scope.$watch('datepickerOptions.initDate', function(initDate) {
37153 if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
37154 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
37155 self.refreshView();
37159 self.activeDate = new Date();
37162 var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
37163 this.activeDate = !isNaN(date) ?
37164 dateParser.fromTimezone(date, ngModelOptions.timezone) :
37165 dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
37167 ngModelCtrl.$render = function() {
37172 this.render = function() {
37173 if (ngModelCtrl.$viewValue) {
37174 var date = new Date(ngModelCtrl.$viewValue),
37175 isValid = !isNaN(date);
37178 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
37179 } else if (!$datepickerSuppressError) {
37180 $log.error('Datepicker directive: "ng-model" value must be a Date object');
37183 this.refreshView();
37186 this.refreshView = function() {
37187 if (this.element) {
37188 $scope.selectedDt = null;
37189 this._refreshView();
37190 if ($scope.activeDt) {
37191 $scope.activeDateId = $scope.activeDt.uid;
37194 var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
37195 date = dateParser.fromTimezone(date, ngModelOptions.timezone);
37196 ngModelCtrl.$setValidity('dateDisabled', !date ||
37197 this.element && !this.isDisabled(date));
37201 this.createDateObject = function(date, format) {
37202 var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
37203 model = dateParser.fromTimezone(model, ngModelOptions.timezone);
37204 var today = new Date();
37205 today = dateParser.fromTimezone(today, ngModelOptions.timezone);
37206 var time = this.compare(date, today);
37209 label: dateParser.filter(date, format),
37210 selected: model && this.compare(date, model) === 0,
37211 disabled: this.isDisabled(date),
37213 current: time === 0,
37215 customClass: this.customClass(date) || null
37218 if (model && this.compare(date, model) === 0) {
37219 $scope.selectedDt = dt;
37222 if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
37223 $scope.activeDt = dt;
37229 this.isDisabled = function(date) {
37230 return $scope.disabled ||
37231 this.minDate && this.compare(date, this.minDate) < 0 ||
37232 this.maxDate && this.compare(date, this.maxDate) > 0 ||
37233 $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
37236 this.customClass = function(date) {
37237 return $scope.customClass({date: date, mode: $scope.datepickerMode});
37240 // Split array into smaller arrays
37241 this.split = function(arr, size) {
37243 while (arr.length > 0) {
37244 arrays.push(arr.splice(0, size));
37249 $scope.select = function(date) {
37250 if ($scope.datepickerMode === self.minMode) {
37251 var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
37252 dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
37253 dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
37254 ngModelCtrl.$setViewValue(dt);
37255 ngModelCtrl.$render();
37257 self.activeDate = date;
37258 setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]);
37260 $scope.$emit('uib:datepicker.mode');
37263 $scope.$broadcast('uib:datepicker.focus');
37266 $scope.move = function(direction) {
37267 var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
37268 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
37269 self.activeDate.setFullYear(year, month, 1);
37270 self.refreshView();
37273 $scope.toggleMode = function(direction) {
37274 direction = direction || 1;
37276 if ($scope.datepickerMode === self.maxMode && direction === 1 ||
37277 $scope.datepickerMode === self.minMode && direction === -1) {
37281 setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]);
37283 $scope.$emit('uib:datepicker.mode');
37285 angular.module('ui.bootstrap.position', [])
37288 * A set of utility methods for working with the DOM.
37289 * It is meant to be used where we need to absolute-position elements in
37290 * relation to another element (this is the case for tooltips, popovers,
37291 * typeahead suggestions etc.).
37293 .factory('$uibPosition', ['$document', '$window', function($document, $window) {
37295 * Used by scrollbarWidth() function to cache scrollbar's width.
37296 * Do not access this variable directly, use scrollbarWidth() instead.
37298 var SCROLLBAR_WIDTH;
37299 var OVERFLOW_REGEX = {
37300 normal: /(auto|scroll)/,
37301 hidden: /(auto|scroll|hidden)/
37303 var PLACEMENT_REGEX = {
37304 auto: /\s?auto?\s?/i,
37305 primary: /^(top|bottom|left|right)$/,
37306 secondary: /^(top|bottom|left|right|center)$/,
37307 vertical: /^(top|bottom)$/
37313 * Provides a raw DOM element from a jQuery/jQLite element.
37315 * @param {element} elem - The element to convert.
37317 * @returns {element} A HTML element.
37319 getRawNode: function(elem) {
37320 return elem[0] || elem;
37324 * Provides a parsed number for a style property. Strips
37325 * units and casts invalid numbers to 0.
37327 * @param {string} value - The style value to parse.
37329 * @returns {number} A valid number.
37331 parseStyle: function(value) {
37332 value = parseFloat(value);
37333 return isFinite(value) ? value : 0;
37337 * Provides the closest positioned ancestor.
37339 * @param {element} element - The element to get the offest parent for.
37341 * @returns {element} The closest positioned ancestor.
37343 offsetParent: function(elem) {
37344 elem = this.getRawNode(elem);
37346 var offsetParent = elem.offsetParent || $document[0].documentElement;
37348 function isStaticPositioned(el) {
37349 return ($window.getComputedStyle(el).position || 'static') === 'static';
37352 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
37353 offsetParent = offsetParent.offsetParent;
37356 return offsetParent || $document[0].documentElement;
37360 * Provides the scrollbar width, concept from TWBS measureScrollbar()
37361 * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
37363 * @returns {number} The width of the browser scollbar.
37365 scrollbarWidth: function() {
37366 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
37367 var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
37368 $document.find('body').append(scrollElem);
37369 SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
37370 SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
37371 scrollElem.remove();
37374 return SCROLLBAR_WIDTH;
37378 * Provides the closest scrollable ancestor.
37379 * A port of the jQuery UI scrollParent method:
37380 * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
37382 * @param {element} elem - The element to find the scroll parent of.
37383 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
37384 * default is false.
37386 * @returns {element} A HTML element.
37388 scrollParent: function(elem, includeHidden) {
37389 elem = this.getRawNode(elem);
37391 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
37392 var documentEl = $document[0].documentElement;
37393 var elemStyle = $window.getComputedStyle(elem);
37394 var excludeStatic = elemStyle.position === 'absolute';
37395 var scrollParent = elem.parentElement || documentEl;
37397 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
37401 while (scrollParent.parentElement && scrollParent !== documentEl) {
37402 var spStyle = $window.getComputedStyle(scrollParent);
37403 if (excludeStatic && spStyle.position !== 'static') {
37404 excludeStatic = false;
37407 if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
37410 scrollParent = scrollParent.parentElement;
37413 return scrollParent;
37417 * Provides read-only equivalent of jQuery's position function:
37418 * http://api.jquery.com/position/ - distance to closest positioned
37419 * ancestor. Does not account for margins by default like jQuery position.
37421 * @param {element} elem - The element to caclulate the position on.
37422 * @param {boolean=} [includeMargins=false] - Should margins be accounted
37423 * for, default is false.
37425 * @returns {object} An object with the following properties:
37427 * <li>**width**: the width of the element</li>
37428 * <li>**height**: the height of the element</li>
37429 * <li>**top**: distance to top edge of offset parent</li>
37430 * <li>**left**: distance to left edge of offset parent</li>
37433 position: function(elem, includeMagins) {
37434 elem = this.getRawNode(elem);
37436 var elemOffset = this.offset(elem);
37437 if (includeMagins) {
37438 var elemStyle = $window.getComputedStyle(elem);
37439 elemOffset.top -= this.parseStyle(elemStyle.marginTop);
37440 elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
37442 var parent = this.offsetParent(elem);
37443 var parentOffset = {top: 0, left: 0};
37445 if (parent !== $document[0].documentElement) {
37446 parentOffset = this.offset(parent);
37447 parentOffset.top += parent.clientTop - parent.scrollTop;
37448 parentOffset.left += parent.clientLeft - parent.scrollLeft;
37452 width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
37453 height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
37454 top: Math.round(elemOffset.top - parentOffset.top),
37455 left: Math.round(elemOffset.left - parentOffset.left)
37460 * Provides read-only equivalent of jQuery's offset function:
37461 * http://api.jquery.com/offset/ - distance to viewport. Does
37462 * not account for borders, margins, or padding on the body
37465 * @param {element} elem - The element to calculate the offset on.
37467 * @returns {object} An object with the following properties:
37469 * <li>**width**: the width of the element</li>
37470 * <li>**height**: the height of the element</li>
37471 * <li>**top**: distance to top edge of viewport</li>
37472 * <li>**right**: distance to bottom edge of viewport</li>
37475 offset: function(elem) {
37476 elem = this.getRawNode(elem);
37478 var elemBCR = elem.getBoundingClientRect();
37480 width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
37481 height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
37482 top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
37483 left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
37488 * Provides offset distance to the closest scrollable ancestor
37489 * or viewport. Accounts for border and scrollbar width.
37491 * Right and bottom dimensions represent the distance to the
37492 * respective edge of the viewport element. If the element
37493 * edge extends beyond the viewport, a negative value will be
37496 * @param {element} elem - The element to get the viewport offset for.
37497 * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
37498 * of the first scrollable element, default is false.
37499 * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
37500 * be accounted for, default is true.
37502 * @returns {object} An object with the following properties:
37504 * <li>**top**: distance to the top content edge of viewport element</li>
37505 * <li>**bottom**: distance to the bottom content edge of viewport element</li>
37506 * <li>**left**: distance to the left content edge of viewport element</li>
37507 * <li>**right**: distance to the right content edge of viewport element</li>
37510 viewportOffset: function(elem, useDocument, includePadding) {
37511 elem = this.getRawNode(elem);
37512 includePadding = includePadding !== false ? true : false;
37514 var elemBCR = elem.getBoundingClientRect();
37515 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
37517 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
37518 var offsetParentBCR = offsetParent.getBoundingClientRect();
37520 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
37521 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
37522 if (offsetParent === $document[0].documentElement) {
37523 offsetBCR.top += $window.pageYOffset;
37524 offsetBCR.left += $window.pageXOffset;
37526 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
37527 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
37529 if (includePadding) {
37530 var offsetParentStyle = $window.getComputedStyle(offsetParent);
37531 offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
37532 offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
37533 offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
37534 offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
37538 top: Math.round(elemBCR.top - offsetBCR.top),
37539 bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
37540 left: Math.round(elemBCR.left - offsetBCR.left),
37541 right: Math.round(offsetBCR.right - elemBCR.right)
37546 * Provides an array of placement values parsed from a placement string.
37547 * Along with the 'auto' indicator, supported placement strings are:
37549 * <li>top: element on top, horizontally centered on host element.</li>
37550 * <li>top-left: element on top, left edge aligned with host element left edge.</li>
37551 * <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
37552 * <li>bottom: element on bottom, horizontally centered on host element.</li>
37553 * <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
37554 * <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
37555 * <li>left: element on left, vertically centered on host element.</li>
37556 * <li>left-top: element on left, top edge aligned with host element top edge.</li>
37557 * <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
37558 * <li>right: element on right, vertically centered on host element.</li>
37559 * <li>right-top: element on right, top edge aligned with host element top edge.</li>
37560 * <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
37562 * A placement string with an 'auto' indicator is expected to be
37563 * space separated from the placement, i.e: 'auto bottom-left' If
37564 * the primary and secondary placement values do not match 'top,
37565 * bottom, left, right' then 'top' will be the primary placement and
37566 * 'center' will be the secondary placement. If 'auto' is passed, true
37567 * will be returned as the 3rd value of the array.
37569 * @param {string} placement - The placement string to parse.
37571 * @returns {array} An array with the following values
37573 * <li>**[0]**: The primary placement.</li>
37574 * <li>**[1]**: The secondary placement.</li>
37575 * <li>**[2]**: If auto is passed: true, else undefined.</li>
37578 parsePlacement: function(placement) {
37579 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
37581 placement = placement.replace(PLACEMENT_REGEX.auto, '');
37584 placement = placement.split('-');
37586 placement[0] = placement[0] || 'top';
37587 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
37588 placement[0] = 'top';
37591 placement[1] = placement[1] || 'center';
37592 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
37593 placement[1] = 'center';
37597 placement[2] = true;
37599 placement[2] = false;
37606 * Provides coordinates for an element to be positioned relative to
37607 * another element. Passing 'auto' as part of the placement parameter
37608 * will enable smart placement - where the element fits. i.e:
37609 * 'auto left-top' will check to see if there is enough space to the left
37610 * of the hostElem to fit the targetElem, if not place right (same for secondary
37611 * top placement). Available space is calculated using the viewportOffset
37614 * @param {element} hostElem - The element to position against.
37615 * @param {element} targetElem - The element to position.
37616 * @param {string=} [placement=top] - The placement for the targetElem,
37617 * default is 'top'. 'center' is assumed as secondary placement for
37618 * 'top', 'left', 'right', and 'bottom' placements. Available placements are:
37621 * <li>top-right</li>
37622 * <li>top-left</li>
37624 * <li>bottom-left</li>
37625 * <li>bottom-right</li>
37627 * <li>left-top</li>
37628 * <li>left-bottom</li>
37630 * <li>right-top</li>
37631 * <li>right-bottom</li>
37633 * @param {boolean=} [appendToBody=false] - Should the top and left values returned
37634 * be calculated from the body element, default is false.
37636 * @returns {object} An object with the following properties:
37638 * <li>**top**: Value for targetElem top.</li>
37639 * <li>**left**: Value for targetElem left.</li>
37640 * <li>**placement**: The resolved placement.</li>
37643 positionElements: function(hostElem, targetElem, placement, appendToBody) {
37644 hostElem = this.getRawNode(hostElem);
37645 targetElem = this.getRawNode(targetElem);
37647 // need to read from prop to support tests.
37648 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
37649 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
37651 placement = this.parsePlacement(placement);
37653 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
37654 var targetElemPos = {top: 0, left: 0, placement: ''};
37656 if (placement[2]) {
37657 var viewportOffset = this.viewportOffset(hostElem);
37659 var targetElemStyle = $window.getComputedStyle(targetElem);
37660 var adjustedSize = {
37661 width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
37662 height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
37665 placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
37666 placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
37667 placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
37668 placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
37671 placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
37672 placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
37673 placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
37674 placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
37677 if (placement[1] === 'center') {
37678 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
37679 var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
37680 if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
37681 placement[1] = 'left';
37682 } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
37683 placement[1] = 'right';
37686 var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
37687 if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
37688 placement[1] = 'top';
37689 } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
37690 placement[1] = 'bottom';
37696 switch (placement[0]) {
37698 targetElemPos.top = hostElemPos.top - targetHeight;
37701 targetElemPos.top = hostElemPos.top + hostElemPos.height;
37704 targetElemPos.left = hostElemPos.left - targetWidth;
37707 targetElemPos.left = hostElemPos.left + hostElemPos.width;
37711 switch (placement[1]) {
37713 targetElemPos.top = hostElemPos.top;
37716 targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
37719 targetElemPos.left = hostElemPos.left;
37722 targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
37725 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
37726 targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
37728 targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
37733 targetElemPos.top = Math.round(targetElemPos.top);
37734 targetElemPos.left = Math.round(targetElemPos.left);
37735 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
37737 return targetElemPos;
37741 * Provides a way for positioning tooltip & dropdown
37742 * arrows when using placement options beyond the standard
37743 * left, right, top, or bottom.
37745 * @param {element} elem - The tooltip/dropdown element.
37746 * @param {string} placement - The placement for the elem.
37748 positionArrow: function(elem, placement) {
37749 elem = this.getRawNode(elem);
37751 var isTooltip = true;
37753 var innerElem = elem.querySelector('.tooltip-inner');
37756 innerElem = elem.querySelector('.popover-inner');
37762 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
37767 placement = this.parsePlacement(placement);
37768 if (placement[1] === 'center') {
37769 // no adjustment necessary - just reset styles
37770 angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
37774 var borderProp = 'border-' + placement[0] + '-width';
37775 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
37777 var borderRadiusProp = 'border-';
37778 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
37779 borderRadiusProp += placement[0] + '-' + placement[1];
37781 borderRadiusProp += placement[1] + '-' + placement[0];
37783 borderRadiusProp += '-radius';
37784 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
37794 switch (placement[0]) {
37796 arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
37799 arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
37802 arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
37805 arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
37809 arrowCss[placement[1]] = borderRadius;
37811 angular.element(arrowElem).css(arrowCss);
37816 angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position'])
37818 .value('$datepickerSuppressError', false)
37820 .constant('uibDatepickerConfig', {
37822 formatMonth: 'MMMM',
37823 formatYear: 'yyyy',
37824 formatDayHeader: 'EEE',
37825 formatDayTitle: 'MMMM yyyy',
37826 formatMonthTitle: 'yyyy',
37827 datepickerMode: 'day',
37836 shortcutPropagation: false,
37840 .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser',
37841 function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) {
37843 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
37844 ngModelOptions = {};
37847 this.modes = ['day', 'month', 'year'];
37849 // Interpolated configuration attributes
37850 angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) {
37851 self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key];
37854 // Evaled configuration attributes
37855 angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) {
37856 self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key];
37859 // Watchable date attributes
37860 angular.forEach(['minDate', 'maxDate'], function(key) {
37862 $scope.$parent.$watch($attrs[key], function(value) {
37863 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null;
37864 self.refreshView();
37867 self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null;
37871 angular.forEach(['minMode', 'maxMode'], function(key) {
37873 $scope.$parent.$watch($attrs[key], function(value) {
37874 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key];
37875 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) ||
37876 key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) {
37877 $scope.datepickerMode = self[key];
37881 self[key] = $scope[key] = datepickerConfig[key] || null;
37885 $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
37886 $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
37888 if (angular.isDefined($attrs.initDate)) {
37889 this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date();
37890 $scope.$parent.$watch($attrs.initDate, function(initDate) {
37891 if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
37892 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
37893 self.refreshView();
37897 this.activeDate = new Date();
37900 $scope.disabled = angular.isDefined($attrs.disabled) || false;
37901 if (angular.isDefined($attrs.ngDisabled)) {
37902 $scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
37903 $scope.disabled = disabled;
37904 self.refreshView();
37908 $scope.isActive = function(dateObject) {
37909 if (self.compare(dateObject.date, self.activeDate) === 0) {
37910 $scope.activeDateId = dateObject.uid;
37916 this.init = function(ngModelCtrl_) {
37917 ngModelCtrl = ngModelCtrl_;
37918 ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
37920 if (ngModelCtrl.$modelValue) {
37921 this.activeDate = ngModelCtrl.$modelValue;
37924 ngModelCtrl.$render = function() {
37929 this.render = function() {
37930 if (ngModelCtrl.$viewValue) {
37931 var date = new Date(ngModelCtrl.$viewValue),
37932 isValid = !isNaN(date);
37935 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
37936 } else if (!$datepickerSuppressError) {
37937 $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
37940 this.refreshView();
37943 this.refreshView = function() {
37944 if (this.element) {
37945 $scope.selectedDt = null;
37946 this._refreshView();
37947 if ($scope.activeDt) {
37948 $scope.activeDateId = $scope.activeDt.uid;
37951 var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
37952 date = dateParser.fromTimezone(date, ngModelOptions.timezone);
37953 ngModelCtrl.$setValidity('dateDisabled', !date ||
37954 this.element && !this.isDisabled(date));
37958 this.createDateObject = function(date, format) {
37959 var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
37960 model = dateParser.fromTimezone(model, ngModelOptions.timezone);
37963 label: dateFilter(date, format),
37964 selected: model && this.compare(date, model) === 0,
37965 disabled: this.isDisabled(date),
37966 current: this.compare(date, new Date()) === 0,
37967 customClass: this.customClass(date) || null
37970 if (model && this.compare(date, model) === 0) {
37971 $scope.selectedDt = dt;
37974 if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
37975 $scope.activeDt = dt;
37981 this.isDisabled = function(date) {
37982 return $scope.disabled ||
37983 this.minDate && this.compare(date, this.minDate) < 0 ||
37984 this.maxDate && this.compare(date, this.maxDate) > 0 ||
37985 $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
37988 this.customClass = function(date) {
37989 return $scope.customClass({date: date, mode: $scope.datepickerMode});
37992 // Split array into smaller arrays
37993 this.split = function(arr, size) {
37995 while (arr.length > 0) {
37996 arrays.push(arr.splice(0, size));
38001 $scope.select = function(date) {
38002 if ($scope.datepickerMode === self.minMode) {
38003 var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
38004 dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
38005 dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
38006 ngModelCtrl.$setViewValue(dt);
38007 ngModelCtrl.$render();
38009 self.activeDate = date;
38010 $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
38014 $scope.move = function(direction) {
38015 var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
38016 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
38017 self.activeDate.setFullYear(year, month, 1);
38018 self.refreshView();
38021 $scope.toggleMode = function(direction) {
38022 direction = direction || 1;
38024 if ($scope.datepickerMode === self.maxMode && direction === 1 ||
38025 $scope.datepickerMode === self.minMode && direction === -1) {
38029 $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
38030 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
38033 // Key event mapper
38034 $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
38036 var focusElement = function() {
38037 self.element[0].focus();
38040 // Listen for focus requests from popup directive
38041 $scope.$on('uib:datepicker.focus', focusElement);
38043 $scope.keydown = function(evt) {
38044 var key = $scope.keys[evt.which];
38046 if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
38050 evt.preventDefault();
38051 if (!self.shortcutPropagation) {
38052 evt.stopPropagation();
38055 if (key === 'enter' || key === 'space') {
38056 if (self.isDisabled(self.activeDate)) {
38057 return; // do nothing
38059 $scope.select(self.activeDate);
38060 } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
38061 $scope.toggleMode(key === 'up' ? 1 : -1);
38063 self.handleKeyDown(key, evt);
38064 self.refreshView();
38069 $scope.$on('$destroy', function() {
38070 //Clear all watch listeners on destroy
38071 while (watchListeners.length) {
38072 watchListeners.shift()();
38076 function setMode(mode) {
38077 $scope.datepickerMode = mode;
38078 $scope.datepickerOptions.datepickerMode = mode;
38081 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
38084 .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
38085 var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
38087 this.step = { months: 1 };
38088 this.element = $element;
38089 function getDaysInMonth(year, month) {
38090 return month === 1 && year % 4 === 0 &&
38091 (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
38094 this.init = function(ctrl) {
38095 angular.extend(ctrl, this);
38096 scope.showWeeks = ctrl.showWeeks;
38097 ctrl.refreshView();
38100 this.getDates = function(startDate, n) {
38101 var dates = new Array(n), current = new Date(startDate), i = 0, date;
38103 date = new Date(current);
38105 current.setDate(current.getDate() + 1);
38110 this._refreshView = function() {
38111 var year = this.activeDate.getFullYear(),
38112 month = this.activeDate.getMonth(),
38113 firstDayOfMonth = new Date(this.activeDate);
38115 firstDayOfMonth.setFullYear(year, month, 1);
38117 var difference = this.startingDay - firstDayOfMonth.getDay(),
38118 numDisplayedFromPreviousMonth = difference > 0 ?
38119 7 - difference : - difference,
38120 firstDate = new Date(firstDayOfMonth);
38122 if (numDisplayedFromPreviousMonth > 0) {
38123 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
38126 // 42 is the number of days on a six-week calendar
38127 var days = this.getDates(firstDate, 42);
38128 for (var i = 0; i < 42; i ++) {
38129 days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
38130 secondary: days[i].getMonth() !== month,
38131 uid: scope.uniqueId + '-' + i
38135 scope.labels = new Array(7);
38136 for (var j = 0; j < 7; j++) {
38137 scope.labels[j] = {
38138 abbr: dateFilter(days[j].date, this.formatDayHeader),
38139 full: dateFilter(days[j].date, 'EEEE')
38143 scope.title = dateFilter(this.activeDate, this.formatDayTitle);
38144 scope.rows = this.split(days, 7);
38146 if (scope.showWeeks) {
38147 scope.weekNumbers = [];
38148 var thursdayIndex = (4 + 7 - this.startingDay) % 7,
38149 numWeeks = scope.rows.length;
38150 for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
38151 scope.weekNumbers.push(
38152 getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
38157 this.compare = function(date1, date2) {
38158 var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
38159 var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
38160 _date1.setFullYear(date1.getFullYear());
38161 _date2.setFullYear(date2.getFullYear());
38162 return _date1 - _date2;
38165 function getISO8601WeekNumber(date) {
38166 var checkDate = new Date(date);
38167 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
38168 var time = checkDate.getTime();
38169 checkDate.setMonth(0); // Compare with Jan 1
38170 checkDate.setDate(1);
38171 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
38174 this.handleKeyDown = function(key, evt) {
38175 var date = this.activeDate.getDate();
38177 if (key === 'left') {
38179 } else if (key === 'up') {
38181 } else if (key === 'right') {
38183 } else if (key === 'down') {
38185 } else if (key === 'pageup' || key === 'pagedown') {
38186 var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
38187 this.activeDate.setMonth(month, 1);
38188 date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
38189 } else if (key === 'home') {
38191 } else if (key === 'end') {
38192 date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
38194 this.activeDate.setDate(date);
38198 .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
38199 this.step = { years: 1 };
38200 this.element = $element;
38202 this.init = function(ctrl) {
38203 angular.extend(ctrl, this);
38204 ctrl.refreshView();
38207 this._refreshView = function() {
38208 var months = new Array(12),
38209 year = this.activeDate.getFullYear(),
38212 for (var i = 0; i < 12; i++) {
38213 date = new Date(this.activeDate);
38214 date.setFullYear(year, i, 1);
38215 months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
38216 uid: scope.uniqueId + '-' + i
38220 scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
38221 scope.rows = this.split(months, 3);
38224 this.compare = function(date1, date2) {
38225 var _date1 = new Date(date1.getFullYear(), date1.getMonth());
38226 var _date2 = new Date(date2.getFullYear(), date2.getMonth());
38227 _date1.setFullYear(date1.getFullYear());
38228 _date2.setFullYear(date2.getFullYear());
38229 return _date1 - _date2;
38232 this.handleKeyDown = function(key, evt) {
38233 var date = this.activeDate.getMonth();
38235 if (key === 'left') {
38237 } else if (key === 'up') {
38239 } else if (key === 'right') {
38241 } else if (key === 'down') {
38243 } else if (key === 'pageup' || key === 'pagedown') {
38244 var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
38245 this.activeDate.setFullYear(year);
38246 } else if (key === 'home') {
38248 } else if (key === 'end') {
38251 this.activeDate.setMonth(date);
38255 .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
38256 var columns, range;
38257 this.element = $element;
38259 function getStartingYear(year) {
38260 return parseInt((year - 1) / range, 10) * range + 1;
38263 this.yearpickerInit = function() {
38264 columns = this.yearColumns;
38265 range = this.yearRows * columns;
38266 this.step = { years: range };
38269 this._refreshView = function() {
38270 var years = new Array(range), date;
38272 for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
38273 date = new Date(this.activeDate);
38274 date.setFullYear(start + i, 0, 1);
38275 years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
38276 uid: scope.uniqueId + '-' + i
38280 scope.title = [years[0].label, years[range - 1].label].join(' - ');
38281 scope.rows = this.split(years, columns);
38282 scope.columns = columns;
38285 this.compare = function(date1, date2) {
38286 return date1.getFullYear() - date2.getFullYear();
38289 this.handleKeyDown = function(key, evt) {
38290 var date = this.activeDate.getFullYear();
38292 if (key === 'left') {
38294 } else if (key === 'up') {
38295 date = date - columns;
38296 } else if (key === 'right') {
38298 } else if (key === 'down') {
38299 date = date + columns;
38300 } else if (key === 'pageup' || key === 'pagedown') {
38301 date += (key === 'pageup' ? - 1 : 1) * range;
38302 } else if (key === 'home') {
38303 date = getStartingYear(this.activeDate.getFullYear());
38304 } else if (key === 'end') {
38305 date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
38307 this.activeDate.setFullYear(date);
38311 .directive('uibDatepicker', function() {
38314 templateUrl: function(element, attrs) {
38315 return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
38319 datepickerOptions: '=?'
38321 datepickerMode: '=?',
38324 shortcutPropagation: '&?'
38325 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
38327 require: ['uibDatepicker', '^ngModel'],
38328 controller: 'UibDatepickerController',
38329 controllerAs: 'datepicker',
38330 link: function(scope, element, attrs, ctrls) {
38331 var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38333 datepickerCtrl.init(ngModelCtrl);
38338 .directive('uibDaypicker', function() {
38341 templateUrl: function(element, attrs) {
38342 return attrs.templateUrl || 'uib/template/datepicker/day.html';
38344 require: ['^uibDatepicker', 'uibDaypicker'],
38345 controller: 'UibDaypickerController',
38346 link: function(scope, element, attrs, ctrls) {
38347 var datepickerCtrl = ctrls[0],
38348 daypickerCtrl = ctrls[1];
38350 daypickerCtrl.init(datepickerCtrl);
38355 .directive('uibMonthpicker', function() {
38358 templateUrl: function(element, attrs) {
38359 return attrs.templateUrl || 'uib/template/datepicker/month.html';
38361 require: ['^uibDatepicker', 'uibMonthpicker'],
38362 controller: 'UibMonthpickerController',
38363 link: function(scope, element, attrs, ctrls) {
38364 var datepickerCtrl = ctrls[0],
38365 monthpickerCtrl = ctrls[1];
38367 monthpickerCtrl.init(datepickerCtrl);
38372 .directive('uibYearpicker', function() {
38375 templateUrl: function(element, attrs) {
38376 return attrs.templateUrl || 'uib/template/datepicker/year.html';
38378 require: ['^uibDatepicker', 'uibYearpicker'],
38379 controller: 'UibYearpickerController',
38380 link: function(scope, element, attrs, ctrls) {
38381 var ctrl = ctrls[0];
38382 angular.extend(ctrl, ctrls[1]);
38383 ctrl.yearpickerInit();
38385 ctrl.refreshView();
38391 angular.module('ui.bootstrap.position', [])
38394 * A set of utility methods for working with the DOM.
38395 * It is meant to be used where we need to absolute-position elements in
38396 * relation to another element (this is the case for tooltips, popovers,
38397 * typeahead suggestions etc.).
38399 .factory('$uibPosition', ['$document', '$window', function($document, $window) {
38401 * Used by scrollbarWidth() function to cache scrollbar's width.
38402 * Do not access this variable directly, use scrollbarWidth() instead.
38404 var SCROLLBAR_WIDTH;
38406 * scrollbar on body and html element in IE and Edge overlay
38407 * content and should be considered 0 width.
38409 var BODY_SCROLLBAR_WIDTH;
38410 var OVERFLOW_REGEX = {
38411 normal: /(auto|scroll)/,
38412 hidden: /(auto|scroll|hidden)/
38414 var PLACEMENT_REGEX = {
38415 auto: /\s?auto?\s?/i,
38416 primary: /^(top|bottom|left|right)$/,
38417 secondary: /^(top|bottom|left|right|center)$/,
38418 vertical: /^(top|bottom)$/
38420 var BODY_REGEX = /(HTML|BODY)/;
38425 * Provides a raw DOM element from a jQuery/jQLite element.
38427 * @param {element} elem - The element to convert.
38429 * @returns {element} A HTML element.
38431 getRawNode: function(elem) {
38432 return elem.nodeName ? elem : elem[0] || elem;
38436 * Provides a parsed number for a style property. Strips
38437 * units and casts invalid numbers to 0.
38439 * @param {string} value - The style value to parse.
38441 * @returns {number} A valid number.
38443 parseStyle: function(value) {
38444 value = parseFloat(value);
38445 return isFinite(value) ? value : 0;
38449 * Provides the closest positioned ancestor.
38451 * @param {element} element - The element to get the offest parent for.
38453 * @returns {element} The closest positioned ancestor.
38455 offsetParent: function(elem) {
38456 elem = this.getRawNode(elem);
38458 var offsetParent = elem.offsetParent || $document[0].documentElement;
38460 function isStaticPositioned(el) {
38461 return ($window.getComputedStyle(el).position || 'static') === 'static';
38464 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
38465 offsetParent = offsetParent.offsetParent;
38468 return offsetParent || $document[0].documentElement;
38472 * Provides the scrollbar width, concept from TWBS measureScrollbar()
38473 * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
38474 * In IE and Edge, scollbar on body and html element overlay and should
38475 * return a width of 0.
38477 * @returns {number} The width of the browser scollbar.
38479 scrollbarWidth: function(isBody) {
38481 if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
38482 var bodyElem = $document.find('body');
38483 bodyElem.addClass('uib-position-body-scrollbar-measure');
38484 BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
38485 BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
38486 bodyElem.removeClass('uib-position-body-scrollbar-measure');
38488 return BODY_SCROLLBAR_WIDTH;
38491 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
38492 var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>');
38493 $document.find('body').append(scrollElem);
38494 SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
38495 SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
38496 scrollElem.remove();
38499 return SCROLLBAR_WIDTH;
38503 * Provides the padding required on an element to replace the scrollbar.
38505 * @returns {object} An object with the following properties:
38507 * <li>**scrollbarWidth**: the width of the scrollbar</li>
38508 * <li>**widthOverflow**: whether the the width is overflowing</li>
38509 * <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li>
38510 * <li>**rightOriginal**: the amount of right padding currently on the element</li>
38511 * <li>**heightOverflow**: whether the the height is overflowing</li>
38512 * <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li>
38513 * <li>**bottomOriginal**: the amount of bottom padding currently on the element</li>
38516 scrollbarPadding: function(elem) {
38517 elem = this.getRawNode(elem);
38519 var elemStyle = $window.getComputedStyle(elem);
38520 var paddingRight = this.parseStyle(elemStyle.paddingRight);
38521 var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
38522 var scrollParent = this.scrollParent(elem, false, true);
38523 var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
38526 scrollbarWidth: scrollbarWidth,
38527 widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
38528 right: paddingRight + scrollbarWidth,
38529 originalRight: paddingRight,
38530 heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
38531 bottom: paddingBottom + scrollbarWidth,
38532 originalBottom: paddingBottom
38537 * Checks to see if the element is scrollable.
38539 * @param {element} elem - The element to check.
38540 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
38541 * default is false.
38543 * @returns {boolean} Whether the element is scrollable.
38545 isScrollable: function(elem, includeHidden) {
38546 elem = this.getRawNode(elem);
38548 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
38549 var elemStyle = $window.getComputedStyle(elem);
38550 return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
38554 * Provides the closest scrollable ancestor.
38555 * A port of the jQuery UI scrollParent method:
38556 * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
38558 * @param {element} elem - The element to find the scroll parent of.
38559 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
38560 * default is false.
38561 * @param {boolean=} [includeSelf=false] - Should the element being passed be
38562 * included in the scrollable llokup.
38564 * @returns {element} A HTML element.
38566 scrollParent: function(elem, includeHidden, includeSelf) {
38567 elem = this.getRawNode(elem);
38569 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
38570 var documentEl = $document[0].documentElement;
38571 var elemStyle = $window.getComputedStyle(elem);
38572 if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
38575 var excludeStatic = elemStyle.position === 'absolute';
38576 var scrollParent = elem.parentElement || documentEl;
38578 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
38582 while (scrollParent.parentElement && scrollParent !== documentEl) {
38583 var spStyle = $window.getComputedStyle(scrollParent);
38584 if (excludeStatic && spStyle.position !== 'static') {
38585 excludeStatic = false;
38588 if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
38591 scrollParent = scrollParent.parentElement;
38594 return scrollParent;
38598 * Provides read-only equivalent of jQuery's position function:
38599 * http://api.jquery.com/position/ - distance to closest positioned
38600 * ancestor. Does not account for margins by default like jQuery position.
38602 * @param {element} elem - The element to caclulate the position on.
38603 * @param {boolean=} [includeMargins=false] - Should margins be accounted
38604 * for, default is false.
38606 * @returns {object} An object with the following properties:
38608 * <li>**width**: the width of the element</li>
38609 * <li>**height**: the height of the element</li>
38610 * <li>**top**: distance to top edge of offset parent</li>
38611 * <li>**left**: distance to left edge of offset parent</li>
38614 position: function(elem, includeMagins) {
38615 elem = this.getRawNode(elem);
38617 var elemOffset = this.offset(elem);
38618 if (includeMagins) {
38619 var elemStyle = $window.getComputedStyle(elem);
38620 elemOffset.top -= this.parseStyle(elemStyle.marginTop);
38621 elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
38623 var parent = this.offsetParent(elem);
38624 var parentOffset = {top: 0, left: 0};
38626 if (parent !== $document[0].documentElement) {
38627 parentOffset = this.offset(parent);
38628 parentOffset.top += parent.clientTop - parent.scrollTop;
38629 parentOffset.left += parent.clientLeft - parent.scrollLeft;
38633 width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
38634 height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
38635 top: Math.round(elemOffset.top - parentOffset.top),
38636 left: Math.round(elemOffset.left - parentOffset.left)
38641 * Provides read-only equivalent of jQuery's offset function:
38642 * http://api.jquery.com/offset/ - distance to viewport. Does
38643 * not account for borders, margins, or padding on the body
38646 * @param {element} elem - The element to calculate the offset on.
38648 * @returns {object} An object with the following properties:
38650 * <li>**width**: the width of the element</li>
38651 * <li>**height**: the height of the element</li>
38652 * <li>**top**: distance to top edge of viewport</li>
38653 * <li>**right**: distance to bottom edge of viewport</li>
38656 offset: function(elem) {
38657 elem = this.getRawNode(elem);
38659 var elemBCR = elem.getBoundingClientRect();
38661 width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
38662 height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
38663 top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
38664 left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
38669 * Provides offset distance to the closest scrollable ancestor
38670 * or viewport. Accounts for border and scrollbar width.
38672 * Right and bottom dimensions represent the distance to the
38673 * respective edge of the viewport element. If the element
38674 * edge extends beyond the viewport, a negative value will be
38677 * @param {element} elem - The element to get the viewport offset for.
38678 * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
38679 * of the first scrollable element, default is false.
38680 * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
38681 * be accounted for, default is true.
38683 * @returns {object} An object with the following properties:
38685 * <li>**top**: distance to the top content edge of viewport element</li>
38686 * <li>**bottom**: distance to the bottom content edge of viewport element</li>
38687 * <li>**left**: distance to the left content edge of viewport element</li>
38688 * <li>**right**: distance to the right content edge of viewport element</li>
38691 viewportOffset: function(elem, useDocument, includePadding) {
38692 elem = this.getRawNode(elem);
38693 includePadding = includePadding !== false ? true : false;
38695 var elemBCR = elem.getBoundingClientRect();
38696 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
38698 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
38699 var offsetParentBCR = offsetParent.getBoundingClientRect();
38701 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
38702 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
38703 if (offsetParent === $document[0].documentElement) {
38704 offsetBCR.top += $window.pageYOffset;
38705 offsetBCR.left += $window.pageXOffset;
38707 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
38708 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
38710 if (includePadding) {
38711 var offsetParentStyle = $window.getComputedStyle(offsetParent);
38712 offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
38713 offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
38714 offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
38715 offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
38719 top: Math.round(elemBCR.top - offsetBCR.top),
38720 bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
38721 left: Math.round(elemBCR.left - offsetBCR.left),
38722 right: Math.round(offsetBCR.right - elemBCR.right)
38727 * Provides an array of placement values parsed from a placement string.
38728 * Along with the 'auto' indicator, supported placement strings are:
38730 * <li>top: element on top, horizontally centered on host element.</li>
38731 * <li>top-left: element on top, left edge aligned with host element left edge.</li>
38732 * <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
38733 * <li>bottom: element on bottom, horizontally centered on host element.</li>
38734 * <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
38735 * <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
38736 * <li>left: element on left, vertically centered on host element.</li>
38737 * <li>left-top: element on left, top edge aligned with host element top edge.</li>
38738 * <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
38739 * <li>right: element on right, vertically centered on host element.</li>
38740 * <li>right-top: element on right, top edge aligned with host element top edge.</li>
38741 * <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
38743 * A placement string with an 'auto' indicator is expected to be
38744 * space separated from the placement, i.e: 'auto bottom-left' If
38745 * the primary and secondary placement values do not match 'top,
38746 * bottom, left, right' then 'top' will be the primary placement and
38747 * 'center' will be the secondary placement. If 'auto' is passed, true
38748 * will be returned as the 3rd value of the array.
38750 * @param {string} placement - The placement string to parse.
38752 * @returns {array} An array with the following values
38754 * <li>**[0]**: The primary placement.</li>
38755 * <li>**[1]**: The secondary placement.</li>
38756 * <li>**[2]**: If auto is passed: true, else undefined.</li>
38759 parsePlacement: function(placement) {
38760 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
38762 placement = placement.replace(PLACEMENT_REGEX.auto, '');
38765 placement = placement.split('-');
38767 placement[0] = placement[0] || 'top';
38768 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
38769 placement[0] = 'top';
38772 placement[1] = placement[1] || 'center';
38773 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
38774 placement[1] = 'center';
38778 placement[2] = true;
38780 placement[2] = false;
38787 * Provides coordinates for an element to be positioned relative to
38788 * another element. Passing 'auto' as part of the placement parameter
38789 * will enable smart placement - where the element fits. i.e:
38790 * 'auto left-top' will check to see if there is enough space to the left
38791 * of the hostElem to fit the targetElem, if not place right (same for secondary
38792 * top placement). Available space is calculated using the viewportOffset
38795 * @param {element} hostElem - The element to position against.
38796 * @param {element} targetElem - The element to position.
38797 * @param {string=} [placement=top] - The placement for the targetElem,
38798 * default is 'top'. 'center' is assumed as secondary placement for
38799 * 'top', 'left', 'right', and 'bottom' placements. Available placements are:
38802 * <li>top-right</li>
38803 * <li>top-left</li>
38805 * <li>bottom-left</li>
38806 * <li>bottom-right</li>
38808 * <li>left-top</li>
38809 * <li>left-bottom</li>
38811 * <li>right-top</li>
38812 * <li>right-bottom</li>
38814 * @param {boolean=} [appendToBody=false] - Should the top and left values returned
38815 * be calculated from the body element, default is false.
38817 * @returns {object} An object with the following properties:
38819 * <li>**top**: Value for targetElem top.</li>
38820 * <li>**left**: Value for targetElem left.</li>
38821 * <li>**placement**: The resolved placement.</li>
38824 positionElements: function(hostElem, targetElem, placement, appendToBody) {
38825 hostElem = this.getRawNode(hostElem);
38826 targetElem = this.getRawNode(targetElem);
38828 // need to read from prop to support tests.
38829 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
38830 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
38832 placement = this.parsePlacement(placement);
38834 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
38835 var targetElemPos = {top: 0, left: 0, placement: ''};
38837 if (placement[2]) {
38838 var viewportOffset = this.viewportOffset(hostElem, appendToBody);
38840 var targetElemStyle = $window.getComputedStyle(targetElem);
38841 var adjustedSize = {
38842 width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
38843 height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
38846 placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
38847 placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
38848 placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
38849 placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
38852 placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
38853 placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
38854 placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
38855 placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
38858 if (placement[1] === 'center') {
38859 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
38860 var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
38861 if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
38862 placement[1] = 'left';
38863 } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
38864 placement[1] = 'right';
38867 var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
38868 if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
38869 placement[1] = 'top';
38870 } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
38871 placement[1] = 'bottom';
38877 switch (placement[0]) {
38879 targetElemPos.top = hostElemPos.top - targetHeight;
38882 targetElemPos.top = hostElemPos.top + hostElemPos.height;
38885 targetElemPos.left = hostElemPos.left - targetWidth;
38888 targetElemPos.left = hostElemPos.left + hostElemPos.width;
38892 switch (placement[1]) {
38894 targetElemPos.top = hostElemPos.top;
38897 targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
38900 targetElemPos.left = hostElemPos.left;
38903 targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
38906 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
38907 targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
38909 targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
38914 targetElemPos.top = Math.round(targetElemPos.top);
38915 targetElemPos.left = Math.round(targetElemPos.left);
38916 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
38918 return targetElemPos;
38922 * Provides a way for positioning tooltip & dropdown
38923 * arrows when using placement options beyond the standard
38924 * left, right, top, or bottom.
38926 * @param {element} elem - The tooltip/dropdown element.
38927 * @param {string} placement - The placement for the elem.
38929 positionArrow: function(elem, placement) {
38930 elem = this.getRawNode(elem);
38932 var innerElem = elem.querySelector('.tooltip-inner, .popover-inner');
38937 var isTooltip = angular.element(innerElem).hasClass('tooltip-inner');
38939 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
38951 placement = this.parsePlacement(placement);
38952 if (placement[1] === 'center') {
38953 // no adjustment necessary - just reset styles
38954 angular.element(arrowElem).css(arrowCss);
38958 var borderProp = 'border-' + placement[0] + '-width';
38959 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
38961 var borderRadiusProp = 'border-';
38962 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
38963 borderRadiusProp += placement[0] + '-' + placement[1];
38965 borderRadiusProp += placement[1] + '-' + placement[0];
38967 borderRadiusProp += '-radius';
38968 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
38970 switch (placement[0]) {
38972 arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
38975 arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
38978 arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
38981 arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
38985 arrowCss[placement[1]] = borderRadius;
38987 angular.element(arrowElem).css(arrowCss);
38992 angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position'])
38994 .value('$datepickerPopupLiteralWarning', true)
38996 .constant('uibDatepickerPopupConfig', {
38997 altInputFormats: [],
38998 appendToBody: false,
38999 clearText: 'Clear',
39000 closeOnDateSelection: true,
39002 currentText: 'Today',
39003 datepickerPopup: 'yyyy-MM-dd',
39004 datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html',
39005 datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
39007 date: 'yyyy-MM-dd',
39008 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
39012 showButtonBar: true,
39013 placement: 'auto bottom-left'
39016 .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning',
39017 function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) {
39019 isHtml5DateInput = false;
39020 var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
39021 datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl,
39022 ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [],
39025 this.init = function(_ngModel_) {
39026 ngModel = _ngModel_;
39027 ngModelOptions = _ngModel_.$options;
39028 closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ?
39029 $scope.$parent.$eval($attrs.closeOnDateSelection) :
39030 datepickerPopupConfig.closeOnDateSelection;
39031 appendToBody = angular.isDefined($attrs.datepickerAppendToBody) ?
39032 $scope.$parent.$eval($attrs.datepickerAppendToBody) :
39033 datepickerPopupConfig.appendToBody;
39034 onOpenFocus = angular.isDefined($attrs.onOpenFocus) ?
39035 $scope.$parent.$eval($attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
39036 datepickerPopupTemplateUrl = angular.isDefined($attrs.datepickerPopupTemplateUrl) ?
39037 $attrs.datepickerPopupTemplateUrl :
39038 datepickerPopupConfig.datepickerPopupTemplateUrl;
39039 datepickerTemplateUrl = angular.isDefined($attrs.datepickerTemplateUrl) ?
39040 $attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
39041 altInputFormats = angular.isDefined($attrs.altInputFormats) ?
39042 $scope.$parent.$eval($attrs.altInputFormats) :
39043 datepickerPopupConfig.altInputFormats;
39045 $scope.showButtonBar = angular.isDefined($attrs.showButtonBar) ?
39046 $scope.$parent.$eval($attrs.showButtonBar) :
39047 datepickerPopupConfig.showButtonBar;
39049 if (datepickerPopupConfig.html5Types[$attrs.type]) {
39050 dateFormat = datepickerPopupConfig.html5Types[$attrs.type];
39051 isHtml5DateInput = true;
39053 dateFormat = $attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
39054 $attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
39055 var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
39056 // Invalidate the $modelValue to ensure that formatters re-run
39057 // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
39058 if (newDateFormat !== dateFormat) {
39059 dateFormat = newDateFormat;
39060 ngModel.$modelValue = null;
39063 throw new Error('uibDatepickerPopup must have a date format specified.');
39070 throw new Error('uibDatepickerPopup must have a date format specified.');
39073 if (isHtml5DateInput && $attrs.uibDatepickerPopup) {
39074 throw new Error('HTML5 date input types do not support custom formats.');
39077 // popup element used to display calendar
39078 popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
39079 if (ngModelOptions) {
39080 timezone = ngModelOptions.timezone;
39081 $scope.ngModelOptions = angular.copy(ngModelOptions);
39082 $scope.ngModelOptions.timezone = null;
39083 if ($scope.ngModelOptions.updateOnDefault === true) {
39084 $scope.ngModelOptions.updateOn = $scope.ngModelOptions.updateOn ?
39085 $scope.ngModelOptions.updateOn + ' default' : 'default';
39088 popupEl.attr('ng-model-options', 'ngModelOptions');
39094 'ng-model': 'date',
39095 'ng-change': 'dateSelection(date)',
39096 'template-url': datepickerPopupTemplateUrl
39099 // datepicker element
39100 datepickerEl = angular.element(popupEl.children()[0]);
39101 datepickerEl.attr('template-url', datepickerTemplateUrl);
39103 if (!$scope.datepickerOptions) {
39104 $scope.datepickerOptions = {};
39107 if (isHtml5DateInput) {
39108 if ($attrs.type === 'month') {
39109 $scope.datepickerOptions.datepickerMode = 'month';
39110 $scope.datepickerOptions.minMode = 'month';
39114 datepickerEl.attr('datepicker-options', 'datepickerOptions');
39116 if (!isHtml5DateInput) {
39117 // Internal API to maintain the correct ng-invalid-[key] class
39118 ngModel.$$parserName = 'date';
39119 ngModel.$validators.date = validator;
39120 ngModel.$parsers.unshift(parseDate);
39121 ngModel.$formatters.push(function(value) {
39122 if (ngModel.$isEmpty(value)) {
39123 $scope.date = value;
39127 if (angular.isNumber(value)) {
39128 value = new Date(value);
39131 $scope.date = dateParser.fromTimezone(value, timezone);
39133 return dateParser.filter($scope.date, dateFormat);
39136 ngModel.$formatters.push(function(value) {
39137 $scope.date = dateParser.fromTimezone(value, timezone);
39142 // Detect changes in the view from the text box
39143 ngModel.$viewChangeListeners.push(function() {
39144 $scope.date = parseDateString(ngModel.$viewValue);
39147 $element.on('keydown', inputKeydownBind);
39149 $popup = $compile(popupEl)($scope);
39150 // Prevent jQuery cache memory leak (template is now redundant after linking)
39153 if (appendToBody) {
39154 $document.find('body').append($popup);
39156 $element.after($popup);
39159 $scope.$on('$destroy', function() {
39160 if ($scope.isOpen === true) {
39161 if (!$rootScope.$$phase) {
39162 $scope.$apply(function() {
39163 $scope.isOpen = false;
39169 $element.off('keydown', inputKeydownBind);
39170 $document.off('click', documentClickBind);
39171 if (scrollParentEl) {
39172 scrollParentEl.off('scroll', positionPopup);
39174 angular.element($window).off('resize', positionPopup);
39176 //Clear all watch listeners on destroy
39177 while (watchListeners.length) {
39178 watchListeners.shift()();
39183 $scope.getText = function(key) {
39184 return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
39187 $scope.isDisabled = function(date) {
39188 if (date === 'today') {
39189 date = dateParser.fromTimezone(new Date(), timezone);
39193 angular.forEach(['minDate', 'maxDate'], function(key) {
39194 if (!$scope.datepickerOptions[key]) {
39196 } else if (angular.isDate($scope.datepickerOptions[key])) {
39197 dates[key] = dateParser.fromTimezone(new Date($scope.datepickerOptions[key]), timezone);
39199 if ($datepickerPopupLiteralWarning) {
39200 $log.warn('Literal date support has been deprecated, please switch to date object usage');
39203 dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium'));
39207 return $scope.datepickerOptions &&
39208 dates.minDate && $scope.compare(date, dates.minDate) < 0 ||
39209 dates.maxDate && $scope.compare(date, dates.maxDate) > 0;
39212 $scope.compare = function(date1, date2) {
39213 return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
39217 $scope.dateSelection = function(dt) {
39218 if (angular.isDefined(dt)) {
39221 var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
39222 $element.val(date);
39223 ngModel.$setViewValue(date);
39225 if (closeOnDateSelection) {
39226 $scope.isOpen = false;
39227 $element[0].focus();
39231 $scope.keydown = function(evt) {
39232 if (evt.which === 27) {
39233 evt.stopPropagation();
39234 $scope.isOpen = false;
39235 $element[0].focus();
39239 $scope.select = function(date, evt) {
39240 evt.stopPropagation();
39242 if (date === 'today') {
39243 var today = new Date();
39244 if (angular.isDate($scope.date)) {
39245 date = new Date($scope.date);
39246 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
39248 date = new Date(today.setHours(0, 0, 0, 0));
39251 $scope.dateSelection(date);
39254 $scope.close = function(evt) {
39255 evt.stopPropagation();
39257 $scope.isOpen = false;
39258 $element[0].focus();
39261 $scope.disabled = angular.isDefined($attrs.disabled) || false;
39262 if ($attrs.ngDisabled) {
39263 watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) {
39264 $scope.disabled = disabled;
39268 $scope.$watch('isOpen', function(value) {
39270 if (!$scope.disabled) {
39271 $timeout(function() {
39275 $scope.$broadcast('uib:datepicker.focus');
39278 $document.on('click', documentClickBind);
39280 var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement;
39281 if (appendToBody || $position.parsePlacement(placement)[2]) {
39282 scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element));
39283 if (scrollParentEl) {
39284 scrollParentEl.on('scroll', positionPopup);
39287 scrollParentEl = null;
39290 angular.element($window).on('resize', positionPopup);
39293 $scope.isOpen = false;
39296 $document.off('click', documentClickBind);
39297 if (scrollParentEl) {
39298 scrollParentEl.off('scroll', positionPopup);
39300 angular.element($window).off('resize', positionPopup);
39304 function cameltoDash(string) {
39305 return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
39308 function parseDateString(viewValue) {
39309 var date = dateParser.parse(viewValue, dateFormat, $scope.date);
39311 for (var i = 0; i < altInputFormats.length; i++) {
39312 date = dateParser.parse(viewValue, altInputFormats[i], $scope.date);
39313 if (!isNaN(date)) {
39321 function parseDate(viewValue) {
39322 if (angular.isNumber(viewValue)) {
39323 // presumably timestamp to date object
39324 viewValue = new Date(viewValue);
39331 if (angular.isDate(viewValue) && !isNaN(viewValue)) {
39335 if (angular.isString(viewValue)) {
39336 var date = parseDateString(viewValue);
39337 if (!isNaN(date)) {
39338 return dateParser.toTimezone(date, timezone);
39342 return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
39345 function validator(modelValue, viewValue) {
39346 var value = modelValue || viewValue;
39348 if (!$attrs.ngRequired && !value) {
39352 if (angular.isNumber(value)) {
39353 value = new Date(value);
39360 if (angular.isDate(value) && !isNaN(value)) {
39364 if (angular.isString(value)) {
39365 return !isNaN(parseDateString(viewValue));
39371 function documentClickBind(event) {
39372 if (!$scope.isOpen && $scope.disabled) {
39376 var popup = $popup[0];
39377 var dpContainsTarget = $element[0].contains(event.target);
39378 // The popup node may not be an element node
39379 // In some browsers (IE) only element nodes have the 'contains' function
39380 var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
39381 if ($scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
39382 $scope.$apply(function() {
39383 $scope.isOpen = false;
39388 function inputKeydownBind(evt) {
39389 if (evt.which === 27 && $scope.isOpen) {
39390 evt.preventDefault();
39391 evt.stopPropagation();
39392 $scope.$apply(function() {
39393 $scope.isOpen = false;
39395 $element[0].focus();
39396 } else if (evt.which === 40 && !$scope.isOpen) {
39397 evt.preventDefault();
39398 evt.stopPropagation();
39399 $scope.$apply(function() {
39400 $scope.isOpen = true;
39405 function positionPopup() {
39406 if ($scope.isOpen) {
39407 var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup'));
39408 var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement;
39409 var position = $position.positionElements($element, dpElement, placement, appendToBody);
39410 dpElement.css({top: position.top + 'px', left: position.left + 'px'});
39411 if (dpElement.hasClass('uib-position-measure')) {
39412 dpElement.removeClass('uib-position-measure');
39417 $scope.$on('uib:datepicker.mode', function() {
39418 $timeout(positionPopup, 0, false);
39424 .constant('uibDatepickerPopupConfig', {
39425 datepickerPopup: 'yyyy-MM-dd',
39426 datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html',
39427 datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
39429 date: 'yyyy-MM-dd',
39430 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
39433 currentText: 'Today',
39434 clearText: 'Clear',
39436 closeOnDateSelection: true,
39437 appendToBody: false,
39438 showButtonBar: true,
39440 altInputFormats: []
39443 .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig',
39444 function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) {
39447 isHtml5DateInput = false;
39448 var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
39449 datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
39450 ngModel, ngModelOptions, $popup, altInputFormats;
39452 scope.watchData = {};
39454 this.init = function(_ngModel_) {
39455 ngModel = _ngModel_;
39456 ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions;
39457 closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
39458 appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
39459 onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
39460 datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
39461 datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
39462 altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
39464 scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
39466 if (datepickerPopupConfig.html5Types[attrs.type]) {
39467 dateFormat = datepickerPopupConfig.html5Types[attrs.type];
39468 isHtml5DateInput = true;
39470 dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
39471 attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
39472 var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
39473 // Invalidate the $modelValue to ensure that formatters re-run
39474 // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
39475 if (newDateFormat !== dateFormat) {
39476 dateFormat = newDateFormat;
39477 ngModel.$modelValue = null;
39480 throw new Error('uibDatepickerPopup must have a date format specified.');
39487 throw new Error('uibDatepickerPopup must have a date format specified.');
39490 if (isHtml5DateInput && attrs.uibDatepickerPopup) {
39491 throw new Error('HTML5 date input types do not support custom formats.');
39494 // popup element used to display calendar
39495 popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
39496 scope.ngModelOptions = angular.copy(ngModelOptions);
39497 scope.ngModelOptions.timezone = null;
39499 'ng-model': 'date',
39500 'ng-model-options': 'ngModelOptions',
39501 'ng-change': 'dateSelection(date)',
39502 'template-url': datepickerPopupTemplateUrl
39505 // datepicker element
39506 datepickerEl = angular.element(popupEl.children()[0]);
39507 datepickerEl.attr('template-url', datepickerTemplateUrl);
39509 if (isHtml5DateInput) {
39510 if (attrs.type === 'month') {
39511 datepickerEl.attr('datepicker-mode', '"month"');
39512 datepickerEl.attr('min-mode', 'month');
39516 if (attrs.datepickerOptions) {
39517 var options = scope.$parent.$eval(attrs.datepickerOptions);
39518 if (options && options.initDate) {
39519 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone);
39520 datepickerEl.attr('init-date', 'initDate');
39521 delete options.initDate;
39523 angular.forEach(options, function(value, option) {
39524 datepickerEl.attr(cameltoDash(option), value);
39528 angular.forEach(['minMode', 'maxMode'], function(key) {
39530 scope.$parent.$watch(function() { return attrs[key]; }, function(value) {
39531 scope.watchData[key] = value;
39533 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
39537 angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) {
39539 var getAttribute = $parse(attrs[key]);
39542 return getAttribute(scope.$parent);
39546 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
39548 // Propagate changes from datepicker to outside
39549 if (key === 'datepickerMode') {
39550 var setAttribute = getAttribute.assign;
39551 propConfig.set = function(v) {
39552 setAttribute(scope.$parent, v);
39556 Object.defineProperty(scope.watchData, key, propConfig);
39560 angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) {
39562 var getAttribute = $parse(attrs[key]);
39564 scope.$parent.$watch(getAttribute, function(value) {
39565 if (key === 'minDate' || key === 'maxDate') {
39566 cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium'));
39569 scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
39572 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
39576 if (attrs.dateDisabled) {
39577 datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
39580 angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) {
39581 if (angular.isDefined(attrs[key])) {
39582 datepickerEl.attr(cameltoDash(key), attrs[key]);
39586 if (attrs.customClass) {
39587 datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
39590 if (!isHtml5DateInput) {
39591 // Internal API to maintain the correct ng-invalid-[key] class
39592 ngModel.$$parserName = 'date';
39593 ngModel.$validators.date = validator;
39594 ngModel.$parsers.unshift(parseDate);
39595 ngModel.$formatters.push(function(value) {
39596 if (ngModel.$isEmpty(value)) {
39597 scope.date = value;
39600 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
39601 return dateFilter(scope.date, dateFormat);
39604 ngModel.$formatters.push(function(value) {
39605 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
39610 // Detect changes in the view from the text box
39611 ngModel.$viewChangeListeners.push(function() {
39612 scope.date = parseDateString(ngModel.$viewValue);
39615 element.bind('keydown', inputKeydownBind);
39617 $popup = $compile(popupEl)(scope);
39618 // Prevent jQuery cache memory leak (template is now redundant after linking)
39621 if (appendToBody) {
39622 $document.find('body').append($popup);
39624 element.after($popup);
39627 scope.$on('$destroy', function() {
39628 if (scope.isOpen === true) {
39629 if (!$rootScope.$$phase) {
39630 scope.$apply(function() {
39631 scope.isOpen = false;
39637 element.unbind('keydown', inputKeydownBind);
39638 $document.unbind('click', documentClickBind);
39642 scope.getText = function(key) {
39643 return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
39646 scope.isDisabled = function(date) {
39647 if (date === 'today') {
39651 return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 ||
39652 scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0;
39655 scope.compare = function(date1, date2) {
39656 return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
39660 scope.dateSelection = function(dt) {
39661 if (angular.isDefined(dt)) {
39664 var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
39666 ngModel.$setViewValue(date);
39668 if (closeOnDateSelection) {
39669 scope.isOpen = false;
39670 element[0].focus();
39674 scope.keydown = function(evt) {
39675 if (evt.which === 27) {
39676 evt.stopPropagation();
39677 scope.isOpen = false;
39678 element[0].focus();
39682 scope.select = function(date) {
39683 if (date === 'today') {
39684 var today = new Date();
39685 if (angular.isDate(scope.date)) {
39686 date = new Date(scope.date);
39687 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
39689 date = new Date(today.setHours(0, 0, 0, 0));
39692 scope.dateSelection(date);
39695 scope.close = function() {
39696 scope.isOpen = false;
39697 element[0].focus();
39700 scope.disabled = angular.isDefined(attrs.disabled) || false;
39701 if (attrs.ngDisabled) {
39702 scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) {
39703 scope.disabled = disabled;
39707 scope.$watch('isOpen', function(value) {
39709 if (!scope.disabled) {
39710 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
39711 scope.position.top = scope.position.top + element.prop('offsetHeight');
39713 $timeout(function() {
39715 scope.$broadcast('uib:datepicker.focus');
39717 $document.bind('click', documentClickBind);
39720 scope.isOpen = false;
39723 $document.unbind('click', documentClickBind);
39727 function cameltoDash(string) {
39728 return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
39731 function parseDateString(viewValue) {
39732 var date = dateParser.parse(viewValue, dateFormat, scope.date);
39734 for (var i = 0; i < altInputFormats.length; i++) {
39735 date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
39736 if (!isNaN(date)) {
39744 function parseDate(viewValue) {
39745 if (angular.isNumber(viewValue)) {
39746 // presumably timestamp to date object
39747 viewValue = new Date(viewValue);
39754 if (angular.isDate(viewValue) && !isNaN(viewValue)) {
39758 if (angular.isString(viewValue)) {
39759 var date = parseDateString(viewValue);
39760 if (!isNaN(date)) {
39761 return dateParser.toTimezone(date, ngModelOptions.timezone);
39765 return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
39768 function validator(modelValue, viewValue) {
39769 var value = modelValue || viewValue;
39771 if (!attrs.ngRequired && !value) {
39775 if (angular.isNumber(value)) {
39776 value = new Date(value);
39783 if (angular.isDate(value) && !isNaN(value)) {
39787 if (angular.isString(value)) {
39788 return !isNaN(parseDateString(viewValue));
39794 function documentClickBind(event) {
39795 if (!scope.isOpen && scope.disabled) {
39799 var popup = $popup[0];
39800 var dpContainsTarget = element[0].contains(event.target);
39801 // The popup node may not be an element node
39802 // In some browsers (IE) only element nodes have the 'contains' function
39803 var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
39804 if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
39805 scope.$apply(function() {
39806 scope.isOpen = false;
39811 function inputKeydownBind(evt) {
39812 if (evt.which === 27 && scope.isOpen) {
39813 evt.preventDefault();
39814 evt.stopPropagation();
39815 scope.$apply(function() {
39816 scope.isOpen = false;
39818 element[0].focus();
39819 } else if (evt.which === 40 && !scope.isOpen) {
39820 evt.preventDefault();
39821 evt.stopPropagation();
39822 scope.$apply(function() {
39823 scope.isOpen = true;
39828 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39830 .directive('uibDatepickerPopup', function() {
39832 require: ['ngModel', 'uibDatepickerPopup'],
39833 controller: 'UibDatepickerPopupController',
39836 datepickerOptions: '=?',
39848 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39850 link: function(scope, element, attrs, ctrls) {
39851 var ngModel = ctrls[0],
39854 ctrl.init(ngModel);
39859 .directive('uibDatepickerPopupWrap', function() {
39863 templateUrl: function(element, attrs) {
39865 return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html';
39867 return attrs.templateUrl || 'uib/template/datepicker/popup.html';
39868 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39873 angular.module('ui.bootstrap.debounce', [])
39875 * A helper, internal service that debounces a function
39877 .factory('$$debounce', ['$timeout', function($timeout) {
39878 return function(callback, debounceTime) {
39879 var timeoutPromise;
39881 return function() {
39883 var args = Array.prototype.slice.call(arguments);
39884 if (timeoutPromise) {
39885 $timeout.cancel(timeoutPromise);
39888 timeoutPromise = $timeout(function() {
39889 callback.apply(self, args);
39895 angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
39897 .constant('uibDropdownConfig', {
39898 appendToOpenClass: 'uib-dropdown-open',
39902 .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
39903 var openScope = null;
39906 this.open = function(dropdownScope, element) {
39908 $document.on('click', closeDropdown);
39909 element.on('keydown', keybindFilter);
39911 this.open = function(dropdownScope) {
39913 $document.on('click', closeDropdown);
39914 $document.on('keydown', keybindFilter);
39915 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39918 if (openScope && openScope !== dropdownScope) {
39919 openScope.isOpen = false;
39922 openScope = dropdownScope;
39926 this.close = function(dropdownScope, element) {
39927 if (openScope === dropdownScope) {
39929 $document.off('click', closeDropdown);
39930 element.off('keydown', keybindFilter);
39932 this.close = function(dropdownScope) {
39933 if (openScope === dropdownScope) {
39935 $document.off('click', closeDropdown);
39936 $document.off('keydown', keybindFilter);
39937 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39941 var closeDropdown = function(evt) {
39942 // This method may still be called during the same mouse event that
39943 // unbound this event handler. So check openScope before proceeding.
39944 if (!openScope) { return; }
39946 if (evt && openScope.getAutoClose() === 'disabled') { return; }
39948 if (evt && evt.which === 3) { return; }
39950 var toggleElement = openScope.getToggleElement();
39951 if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
39955 var dropdownElement = openScope.getDropdownElement();
39956 if (evt && openScope.getAutoClose() === 'outsideClick' &&
39957 dropdownElement && dropdownElement[0].contains(evt.target)) {
39961 openScope.isOpen = false;
39963 if (!$rootScope.$$phase) {
39964 openScope.$apply();
39968 var keybindFilter = function(evt) {
39969 if (evt.which === 27) {
39971 evt.stopPropagation();
39973 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
39974 openScope.focusToggleElement();
39976 } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
39977 evt.preventDefault();
39978 evt.stopPropagation();
39979 openScope.focusDropdownEntry(evt.which);
39984 .controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
39986 scope = $scope.$new(), // create a child scope so we are not polluting original one
39988 appendToOpenClass = dropdownConfig.appendToOpenClass,
39989 openClass = dropdownConfig.openClass,
39991 setIsOpen = angular.noop,
39992 toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
39993 appendToBody = false,
39995 keynavEnabled = false,
39996 selectedOption = null,
39997 body = $document.find('body');
39999 $element.addClass('dropdown');
40001 this.init = function() {
40002 if ($attrs.isOpen) {
40003 getIsOpen = $parse($attrs.isOpen);
40004 setIsOpen = getIsOpen.assign;
40006 $scope.$watch(getIsOpen, function(value) {
40007 scope.isOpen = !!value;
40011 if (angular.isDefined($attrs.dropdownAppendTo)) {
40012 var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
40014 appendTo = angular.element(appendToEl);
40018 appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
40019 keynavEnabled = angular.isDefined($attrs.keyboardNav);
40021 if (appendToBody && !appendTo) {
40025 if (appendTo && self.dropdownMenu) {
40026 appendTo.append(self.dropdownMenu);
40027 $element.on('$destroy', function handleDestroyEvent() {
40028 self.dropdownMenu.remove();
40033 this.toggle = function(open) {
40035 scope.isOpen = arguments.length ? !!open : !scope.isOpen;
40036 if (angular.isFunction(setIsOpen)) {
40037 setIsOpen(scope, scope.isOpen);
40040 return scope.isOpen;
40042 return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
40043 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40046 // Allow other directives to watch status
40047 this.isOpen = function() {
40048 return scope.isOpen;
40051 scope.getToggleElement = function() {
40052 return self.toggleElement;
40055 scope.getAutoClose = function() {
40056 return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
40059 scope.getElement = function() {
40063 scope.isKeynavEnabled = function() {
40064 return keynavEnabled;
40067 scope.focusDropdownEntry = function(keyCode) {
40068 var elems = self.dropdownMenu ? //If append to body is used.
40069 angular.element(self.dropdownMenu).find('a') :
40070 $element.find('ul').eq(0).find('a');
40074 if (!angular.isNumber(self.selectedOption)) {
40075 self.selectedOption = 0;
40077 self.selectedOption = self.selectedOption === elems.length - 1 ?
40078 self.selectedOption :
40079 self.selectedOption + 1;
40084 if (!angular.isNumber(self.selectedOption)) {
40085 self.selectedOption = elems.length - 1;
40087 self.selectedOption = self.selectedOption === 0 ?
40088 0 : self.selectedOption - 1;
40093 elems[self.selectedOption].focus();
40096 scope.getDropdownElement = function() {
40097 return self.dropdownMenu;
40100 scope.focusToggleElement = function() {
40101 if (self.toggleElement) {
40102 self.toggleElement[0].focus();
40106 scope.$watch('isOpen', function(isOpen, wasOpen) {
40107 if (appendTo && self.dropdownMenu) {
40108 var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
40115 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40118 top: pos.top + 'px',
40119 display: isOpen ? 'block' : 'none'
40122 rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
40124 css.left = pos.left + 'px';
40125 css.right = 'auto';
40129 scrollbarWidth = $position.scrollbarWidth(true);
40130 css.right = window.innerWidth - scrollbarWidth -
40132 css.right = window.innerWidth -
40133 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40134 (pos.left + $element.prop('offsetWidth')) + 'px';
40137 // Need to adjust our positioning to be relative to the appendTo container
40138 // if it's not the body element
40139 if (!appendToBody) {
40140 var appendOffset = $position.offset(appendTo);
40142 css.top = pos.top - appendOffset.top + 'px';
40145 css.left = pos.left - appendOffset.left + 'px';
40147 css.right = window.innerWidth -
40148 (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
40152 self.dropdownMenu.css(css);
40155 var openContainer = appendTo ? appendTo : $element;
40157 var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);
40159 if (hasOpenClass === !isOpen) {
40160 $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
40161 if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
40162 toggleInvoker($scope, { open: !!isOpen });
40168 $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
40169 if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
40170 toggleInvoker($scope, { open: !!isOpen });
40173 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40176 if (self.dropdownMenuTemplateUrl) {
40177 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
40178 templateScope = scope.$new();
40179 $compile(tplContent.trim())(templateScope, function(dropdownElement) {
40180 var newEl = dropdownElement;
40181 self.dropdownMenu.replaceWith(newEl);
40182 self.dropdownMenu = newEl;
40187 scope.focusToggleElement();
40189 uibDropdownService.open(scope, $element);
40191 uibDropdownService.open(scope);
40192 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40194 if (self.dropdownMenuTemplateUrl) {
40195 if (templateScope) {
40196 templateScope.$destroy();
40198 var newEl = angular.element('<ul class="dropdown-menu"></ul>');
40199 self.dropdownMenu.replaceWith(newEl);
40200 self.dropdownMenu = newEl;
40204 uibDropdownService.close(scope, $element);
40206 uibDropdownService.close(scope);
40207 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40208 self.selectedOption = null;
40211 if (angular.isFunction(setIsOpen)) {
40212 setIsOpen($scope, isOpen);
40218 $scope.$on('$locationChangeSuccess', function() {
40219 if (scope.getAutoClose() !== 'disabled') {
40220 scope.isOpen = false;
40223 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40226 .directive('uibDropdown', function() {
40228 controller: 'UibDropdownController',
40229 link: function(scope, element, attrs, dropdownCtrl) {
40230 dropdownCtrl.init();
40235 .directive('uibDropdownMenu', function() {
40238 require: '?^uibDropdown',
40239 link: function(scope, element, attrs, dropdownCtrl) {
40240 if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
40244 element.addClass('dropdown-menu');
40246 var tplUrl = attrs.templateUrl;
40248 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
40251 if (!dropdownCtrl.dropdownMenu) {
40252 dropdownCtrl.dropdownMenu = element;
40258 .directive('uibDropdownToggle', function() {
40260 require: '?^uibDropdown',
40261 link: function(scope, element, attrs, dropdownCtrl) {
40262 if (!dropdownCtrl) {
40266 element.addClass('dropdown-toggle');
40268 dropdownCtrl.toggleElement = element;
40270 var toggleDropdown = function(event) {
40271 event.preventDefault();
40273 if (!element.hasClass('disabled') && !attrs.disabled) {
40274 scope.$apply(function() {
40275 dropdownCtrl.toggle();
40280 element.bind('click', toggleDropdown);
40283 element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
40284 scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
40285 element.attr('aria-expanded', !!isOpen);
40288 scope.$on('$destroy', function() {
40289 element.unbind('click', toggleDropdown);
40295 angular.module('ui.bootstrap.stackedMap', [])
40297 * A helper, internal data structure that acts as a map but also allows getting / removing
40298 * elements in the LIFO order
40300 .factory('$$stackedMap', function() {
40302 createNew: function() {
40306 add: function(key, value) {
40312 get: function(key) {
40313 for (var i = 0; i < stack.length; i++) {
40314 if (key === stack[i].key) {
40321 for (var i = 0; i < stack.length; i++) {
40322 keys.push(stack[i].key);
40327 return stack[stack.length - 1];
40329 remove: function(key) {
40331 for (var i = 0; i < stack.length; i++) {
40332 if (key === stack[i].key) {
40337 return stack.splice(idx, 1)[0];
40339 removeTop: function() {
40340 return stack.splice(stack.length - 1, 1)[0];
40342 length: function() {
40343 return stack.length;
40350 angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
40352 angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
40353 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40355 * A helper, internal data structure that stores all references attached to key
40357 .factory('$$multiMap', function() {
40359 createNew: function() {
40363 entries: function() {
40364 return Object.keys(map).map(function(key) {
40371 get: function(key) {
40374 hasKey: function(key) {
40378 return Object.keys(map);
40380 put: function(key, value) {
40385 map[key].push(value);
40387 remove: function(key, value) {
40388 var values = map[key];
40394 var idx = values.indexOf(value);
40397 values.splice(idx, 1);
40400 if (!values.length) {
40410 * Pluggable resolve mechanism for the modal resolve resolution
40411 * Supports UI Router's $resolve service
40413 .provider('$uibResolve', function() {
40414 var resolve = this;
40415 this.resolver = null;
40417 this.setResolver = function(resolver) {
40418 this.resolver = resolver;
40421 this.$get = ['$injector', '$q', function($injector, $q) {
40422 var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
40424 resolve: function(invocables, locals, parent, self) {
40426 return resolver.resolve(invocables, locals, parent, self);
40431 angular.forEach(invocables, function(value) {
40432 if (angular.isFunction(value) || angular.isArray(value)) {
40433 promises.push($q.resolve($injector.invoke(value)));
40434 } else if (angular.isString(value)) {
40435 promises.push($q.resolve($injector.get(value)));
40437 promises.push($q.resolve(value));
40441 return $q.all(promises).then(function(resolves) {
40442 var resolveObj = {};
40443 var resolveIter = 0;
40444 angular.forEach(invocables, function(value, key) {
40445 resolveObj[key] = resolves[resolveIter++];
40456 * A helper directive for the $modal service. It creates a backdrop element.
40459 .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack',
40460 function($animate, $injector, $modalStack) {
40462 .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack',
40463 function($animateCss, $injector, $modalStack) {
40464 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40467 templateUrl: 'uib/template/modal/backdrop.html',
40468 compile: function(tElement, tAttrs) {
40469 tElement.addClass(tAttrs.backdropClass);
40474 function linkFn(scope, element, attrs) {
40475 if (attrs.modalInClass) {
40477 $animate.addClass(element, attrs.modalInClass);
40479 $animateCss(element, {
40480 addClass: attrs.modalInClass
40482 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40484 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
40485 var done = setIsAsync();
40486 if (scope.modalOptions.animation) {
40488 $animate.removeClass(element, attrs.modalInClass).then(done);
40490 $animateCss(element, {
40491 removeClass: attrs.modalInClass
40492 }).start().then(done);
40493 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40503 .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document',
40504 function($modalStack, $q, $animateCss, $document) {
40506 .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
40507 function($modalStack, $q, $animate, $animateCss, $document) {
40508 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40515 templateUrl: function(tElement, tAttrs) {
40516 return tAttrs.templateUrl || 'uib/template/modal/window.html';
40518 link: function(scope, element, attrs) {
40519 element.addClass(attrs.windowClass || '');
40520 element.addClass(attrs.windowTopClass || '');
40521 scope.size = attrs.size;
40523 scope.close = function(evt) {
40524 var modal = $modalStack.getTop();
40525 if (modal && modal.value.backdrop &&
40526 modal.value.backdrop !== 'static' &&
40527 evt.target === evt.currentTarget) {
40528 evt.preventDefault();
40529 evt.stopPropagation();
40530 $modalStack.dismiss(modal.key, 'backdrop click');
40534 // moved from template to fix issue #2280
40535 element.on('click', scope.close);
40537 // This property is only added to the scope for the purpose of detecting when this directive is rendered.
40538 // We can detect that by using this property in the template associated with this directive and then use
40539 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
40540 scope.$isRendered = true;
40542 // Deferred object that will be resolved when this modal is render.
40543 var modalRenderDeferObj = $q.defer();
40544 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
40545 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
40546 attrs.$observe('modalRender', function(value) {
40547 if (value === 'true') {
40548 modalRenderDeferObj.resolve();
40552 modalRenderDeferObj.promise.then(function() {
40553 var animationPromise = null;
40555 if (attrs.modalInClass) {
40556 animationPromise = $animateCss(element, {
40557 addClass: attrs.modalInClass
40560 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
40561 var done = setIsAsync();
40563 $animateCss(element, {
40564 removeClass: attrs.modalInClass
40565 }).start().then(done);
40568 $animateCss(element, {
40569 removeClass: attrs.modalInClass
40570 }).start().then(done);
40572 $animate.removeClass(element, attrs.modalInClass).then(done);
40574 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40579 $q.when(animationPromise).then(function() {
40581 // Notify {@link $modalStack} that modal is rendered.
40582 var modal = $modalStack.getTop();
40584 $modalStack.modalRendered(modal.key);
40588 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40590 * If something within the freshly-opened modal already has focus (perhaps via a
40591 * directive that causes focus). then no need to try and focus anything.
40593 if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
40594 var inputWithAutofocus = element[0].querySelector('[autofocus]');
40596 * Auto-focusing of a freshly-opened modal element causes any child elements
40597 * with the autofocus attribute to lose focus. This is an issue on touch
40598 * based devices which will show and then hide the onscreen keyboard.
40599 * Attempts to refocus the autofocus element via JavaScript will not reopen
40600 * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
40601 * the modal element if the modal does not contain an autofocus element.
40603 if (inputWithAutofocus) {
40604 inputWithAutofocus.focus();
40606 element[0].focus();
40613 // Notify {@link $modalStack} that modal is rendered.
40614 var modal = $modalStack.getTop();
40616 $modalStack.modalRendered(modal.key);
40618 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40624 .directive('uibModalAnimationClass', function() {
40626 compile: function(tElement, tAttrs) {
40627 if (tAttrs.modalAnimation) {
40628 tElement.addClass(tAttrs.uibModalAnimationClass);
40634 .directive('uibModalTransclude', function() {
40636 link: function(scope, element, attrs, controller, transclude) {
40637 transclude(scope.$parent, function(clone) {
40639 element.append(clone);
40645 .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
40647 '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition',
40648 function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) {
40650 '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap',
40651 function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) {
40652 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40653 var OPENED_MODAL_CLASS = 'modal-open';
40655 var backdropDomEl, backdropScope;
40656 var openedWindows = $$stackedMap.createNew();
40657 var openedClasses = $$multiMap.createNew();
40658 var $modalStack = {
40659 NOW_CLOSING_EVENT: 'modal.stack.now-closing'
40662 var topModalIndex = 0;
40663 var previousTopOpenedModal = null;
40665 //Modal focus behavior
40666 var tabableSelector = 'a[href], area[href], input:not([disabled]), ' +
40667 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
40668 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
40669 var scrollbarPadding;
40671 function isVisible(element) {
40672 return !!(element.offsetWidth ||
40673 element.offsetHeight ||
40674 element.getClientRects().length);
40678 //Modal focus behavior
40679 var focusableElementList;
40680 var focusIndex = 0;
40681 var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
40682 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
40683 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
40684 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40686 function backdropIndex() {
40687 var topBackdropIndex = -1;
40688 var opened = openedWindows.keys();
40689 for (var i = 0; i < opened.length; i++) {
40690 if (openedWindows.get(opened[i]).value.backdrop) {
40691 topBackdropIndex = i;
40696 // If any backdrop exist, ensure that it's index is always
40697 // right below the top modal
40698 if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) {
40699 topBackdropIndex = topModalIndex;
40702 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40703 return topBackdropIndex;
40706 $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
40707 if (backdropScope) {
40708 backdropScope.index = newBackdropIndex;
40712 function removeModalWindow(modalInstance, elementToReceiveFocus) {
40713 var modalWindow = openedWindows.get(modalInstance).value;
40714 var appendToElement = modalWindow.appendTo;
40716 //clean up the stack
40717 openedWindows.remove(modalInstance);
40719 previousTopOpenedModal = openedWindows.top();
40720 if (previousTopOpenedModal) {
40721 topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10);
40724 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40726 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
40727 var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
40728 openedClasses.remove(modalBodyClass, modalInstance);
40730 var areAnyOpen = openedClasses.hasKey(modalBodyClass);
40731 appendToElement.toggleClass(modalBodyClass, areAnyOpen);
40732 if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
40733 if (scrollbarPadding.originalRight) {
40734 appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'});
40736 appendToElement.css({paddingRight: ''});
40738 scrollbarPadding = null;
40740 toggleTopWindowClass(true);
40741 }, modalWindow.closedDeferred);
40743 appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
40744 toggleTopWindowClass(true);
40746 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40747 checkRemoveBackdrop();
40749 //move focus to specified element if available, or else to body
40750 if (elementToReceiveFocus && elementToReceiveFocus.focus) {
40751 elementToReceiveFocus.focus();
40752 } else if (appendToElement.focus) {
40753 appendToElement.focus();
40757 // Add or remove "windowTopClass" from the top window in the stack
40758 function toggleTopWindowClass(toggleSwitch) {
40761 if (openedWindows.length() > 0) {
40762 modalWindow = openedWindows.top().value;
40763 modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
40767 function checkRemoveBackdrop() {
40768 //remove backdrop if no longer needed
40769 if (backdropDomEl && backdropIndex() === -1) {
40770 var backdropScopeRef = backdropScope;
40771 removeAfterAnimate(backdropDomEl, backdropScope, function() {
40772 backdropScopeRef = null;
40774 backdropDomEl = undefined;
40775 backdropScope = undefined;
40779 function removeAfterAnimate(domEl, scope, done, closedDeferred) {
40781 var asyncPromise = null;
40782 var setIsAsync = function() {
40783 if (!asyncDeferred) {
40784 asyncDeferred = $q.defer();
40785 asyncPromise = asyncDeferred.promise;
40788 return function asyncDone() {
40789 asyncDeferred.resolve();
40792 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
40794 // Note that it's intentional that asyncPromise might be null.
40795 // That's when setIsAsync has not been called during the
40796 // NOW_CLOSING_EVENT broadcast.
40797 return $q.when(asyncPromise).then(afterAnimating);
40799 function afterAnimating() {
40800 if (afterAnimating.done) {
40803 afterAnimating.done = true;
40806 $animate.leave(domEl).then(function() {
40808 $animateCss(domEl, {
40810 }).start().then(function() {
40811 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40813 if (closedDeferred) {
40814 closedDeferred.resolve();
40825 $document.on('keydown', keydownListener);
40827 $rootScope.$on('$destroy', function() {
40828 $document.off('keydown', keydownListener);
40831 function keydownListener(evt) {
40832 if (evt.isDefaultPrevented()) {
40836 var modal = openedWindows.top();
40838 switch (evt.which) {
40840 if (modal.value.keyboard) {
40841 evt.preventDefault();
40842 $rootScope.$apply(function() {
40843 $modalStack.dismiss(modal.key, 'escape key press');
40850 var list = $modalStack.loadFocusElementList(modal);
40851 var focusChanged = false;
40852 if (evt.shiftKey) {
40853 if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) {
40854 focusChanged = $modalStack.focusLastFocusableElement(list);
40857 if ($modalStack.isFocusInLastItem(evt, list)) {
40858 focusChanged = $modalStack.focusFirstFocusableElement(list);
40860 $modalStack.loadFocusElementList(modal);
40861 var focusChanged = false;
40862 if (evt.shiftKey) {
40863 if ($modalStack.isFocusInFirstItem(evt)) {
40864 focusChanged = $modalStack.focusLastFocusableElement();
40867 if ($modalStack.isFocusInLastItem(evt)) {
40868 focusChanged = $modalStack.focusFirstFocusableElement();
40869 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40873 if (focusChanged) {
40874 evt.preventDefault();
40875 evt.stopPropagation();
40880 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40887 $modalStack.open = function(modalInstance, modal) {
40888 var modalOpener = $document[0].activeElement,
40889 modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
40891 toggleTopWindowClass(false);
40894 // Store the current top first, to determine what index we ought to use
40895 // for the current top modal
40896 previousTopOpenedModal = openedWindows.top();
40899 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40900 openedWindows.add(modalInstance, {
40901 deferred: modal.deferred,
40902 renderDeferred: modal.renderDeferred,
40903 closedDeferred: modal.closedDeferred,
40904 modalScope: modal.scope,
40905 backdrop: modal.backdrop,
40906 keyboard: modal.keyboard,
40907 openedClass: modal.openedClass,
40908 windowTopClass: modal.windowTopClass,
40909 animation: modal.animation,
40910 appendTo: modal.appendTo
40913 openedClasses.put(modalBodyClass, modalInstance);
40915 var appendToElement = modal.appendTo,
40916 currBackdropIndex = backdropIndex();
40918 if (!appendToElement.length) {
40919 throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
40922 if (currBackdropIndex >= 0 && !backdropDomEl) {
40923 backdropScope = $rootScope.$new(true);
40924 backdropScope.modalOptions = modal;
40925 backdropScope.index = currBackdropIndex;
40926 backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
40927 backdropDomEl.attr('backdrop-class', modal.backdropClass);
40928 if (modal.animation) {
40929 backdropDomEl.attr('modal-animation', 'true');
40931 $compile(backdropDomEl)(backdropScope);
40932 $animate.enter(backdropDomEl, appendToElement);
40934 scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
40935 if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
40936 appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
40940 // Set the top modal index based on the index of the previous top modal
40941 topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
40945 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40946 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
40947 angularDomEl.attr({
40948 'template-url': modal.windowTemplateUrl,
40949 'window-class': modal.windowClass,
40950 'window-top-class': modal.windowTopClass,
40951 'size': modal.size,
40953 'index': topModalIndex,
40955 'index': openedWindows.length() - 1,
40956 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40957 'animate': 'animate'
40958 }).html(modal.content);
40959 if (modal.animation) {
40960 angularDomEl.attr('modal-animation', 'true');
40964 appendToElement.addClass(modalBodyClass);
40965 $animate.enter($compile(angularDomEl)(modal.scope), appendToElement);
40967 openedWindows.top().value.modalDomEl = angularDomEl;
40968 openedWindows.top().value.modalOpener = modalOpener;
40970 $animate.enter(angularDomEl, appendToElement)
40972 $compile(angularDomEl)(modal.scope);
40973 $animate.addClass(appendToElement, modalBodyClass);
40976 openedWindows.top().value.modalDomEl = angularDomEl;
40977 openedWindows.top().value.modalOpener = modalOpener;
40979 $modalStack.clearFocusListCache();
40980 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
40983 function broadcastClosing(modalWindow, resultOrReason, closing) {
40984 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
40987 $modalStack.close = function(modalInstance, result) {
40988 var modalWindow = openedWindows.get(modalInstance);
40989 if (modalWindow && broadcastClosing(modalWindow, result, true)) {
40990 modalWindow.value.modalScope.$$uibDestructionScheduled = true;
40991 modalWindow.value.deferred.resolve(result);
40992 removeModalWindow(modalInstance, modalWindow.value.modalOpener);
40995 return !modalWindow;
40998 $modalStack.dismiss = function(modalInstance, reason) {
40999 var modalWindow = openedWindows.get(modalInstance);
41000 if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
41001 modalWindow.value.modalScope.$$uibDestructionScheduled = true;
41002 modalWindow.value.deferred.reject(reason);
41003 removeModalWindow(modalInstance, modalWindow.value.modalOpener);
41006 return !modalWindow;
41009 $modalStack.dismissAll = function(reason) {
41010 var topModal = this.getTop();
41011 while (topModal && this.dismiss(topModal.key, reason)) {
41012 topModal = this.getTop();
41016 $modalStack.getTop = function() {
41017 return openedWindows.top();
41020 $modalStack.modalRendered = function(modalInstance) {
41021 var modalWindow = openedWindows.get(modalInstance);
41023 modalWindow.value.renderDeferred.resolve();
41028 $modalStack.focusFirstFocusableElement = function(list) {
41029 if (list.length > 0) {
41032 $modalStack.focusFirstFocusableElement = function() {
41033 if (focusableElementList.length > 0) {
41034 focusableElementList[0].focus();
41035 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41042 $modalStack.focusLastFocusableElement = function(list) {
41043 if (list.length > 0) {
41044 list[list.length - 1].focus();
41046 $modalStack.focusLastFocusableElement = function() {
41047 if (focusableElementList.length > 0) {
41048 focusableElementList[focusableElementList.length - 1].focus();
41049 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41056 $modalStack.isModalFocused = function(evt, modalWindow) {
41057 if (evt && modalWindow) {
41058 var modalDomEl = modalWindow.value.modalDomEl;
41059 if (modalDomEl && modalDomEl.length) {
41060 return (evt.target || evt.srcElement) === modalDomEl[0];
41063 $modalStack.isFocusInFirstItem = function(evt) {
41064 if (focusableElementList.length > 0) {
41065 return (evt.target || evt.srcElement) === focusableElementList[0];
41066 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41072 $modalStack.isFocusInFirstItem = function(evt, list) {
41073 if (list.length > 0) {
41074 return (evt.target || evt.srcElement) === list[0];
41076 $modalStack.isFocusInLastItem = function(evt) {
41077 if (focusableElementList.length > 0) {
41078 return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
41079 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41085 $modalStack.isFocusInLastItem = function(evt, list) {
41086 if (list.length > 0) {
41087 return (evt.target || evt.srcElement) === list[list.length - 1];
41092 $modalStack.loadFocusElementList = function(modalWindow) {
41094 var modalDomE1 = modalWindow.value.modalDomEl;
41095 if (modalDomE1 && modalDomE1.length) {
41096 var elements = modalDomE1[0].querySelectorAll(tabableSelector);
41098 Array.prototype.filter.call(elements, function(element) {
41099 return isVisible(element);
41102 $modalStack.clearFocusListCache = function() {
41103 focusableElementList = [];
41107 $modalStack.loadFocusElementList = function(modalWindow) {
41108 if (focusableElementList === undefined || !focusableElementList.length) {
41110 var modalDomE1 = modalWindow.value.modalDomEl;
41111 if (modalDomE1 && modalDomE1.length) {
41112 focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
41114 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41119 return $modalStack;
41122 .provider('$uibModal', function() {
41123 var $modalProvider = {
41126 backdrop: true, //can also be false or 'static'
41129 $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
41130 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
41133 function getTemplatePromise(options) {
41134 return options.template ? $q.when(options.template) :
41135 $templateRequest(angular.isFunction(options.templateUrl) ?
41136 options.templateUrl() : options.templateUrl);
41139 var promiseChain = null;
41140 $modal.getPromiseChain = function() {
41141 return promiseChain;
41144 $modal.open = function(modalOptions) {
41145 var modalResultDeferred = $q.defer();
41146 var modalOpenedDeferred = $q.defer();
41147 var modalClosedDeferred = $q.defer();
41148 var modalRenderDeferred = $q.defer();
41150 //prepare an instance of a modal to be injected into controllers and returned to a caller
41151 var modalInstance = {
41152 result: modalResultDeferred.promise,
41153 opened: modalOpenedDeferred.promise,
41154 closed: modalClosedDeferred.promise,
41155 rendered: modalRenderDeferred.promise,
41156 close: function (result) {
41157 return $modalStack.close(modalInstance, result);
41159 dismiss: function (reason) {
41160 return $modalStack.dismiss(modalInstance, reason);
41164 //merge and clean up options
41165 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
41166 modalOptions.resolve = modalOptions.resolve || {};
41167 modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
41170 if (!modalOptions.template && !modalOptions.templateUrl) {
41171 throw new Error('One of template or templateUrl options is required.');
41174 var templateAndResolvePromise =
41175 $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
41177 function resolveWithTemplate() {
41178 return templateAndResolvePromise;
41181 // Wait for the resolution of the existing promise chain.
41182 // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
41183 // Then add to $modalStack and resolve opened.
41184 // Finally clean up the chain variable if no subsequent modal has overwritten it.
41186 samePromise = promiseChain = $q.all([promiseChain])
41187 .then(resolveWithTemplate, resolveWithTemplate)
41188 .then(function resolveSuccess(tplAndVars) {
41189 var providedScope = modalOptions.scope || $rootScope;
41191 var modalScope = providedScope.$new();
41192 modalScope.$close = modalInstance.close;
41193 modalScope.$dismiss = modalInstance.dismiss;
41195 modalScope.$on('$destroy', function() {
41196 if (!modalScope.$$uibDestructionScheduled) {
41197 modalScope.$dismiss('$uibUnscheduledDestruction');
41202 var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
41204 var ctrlInstance, ctrlLocals = {};
41205 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41208 if (modalOptions.controller) {
41209 ctrlLocals.$scope = modalScope;
41211 ctrlLocals.$scope.$resolve = {};
41212 ctrlLocals.$uibModalInstance = modalInstance;
41213 angular.forEach(tplAndVars[1], function(value, key) {
41214 ctrlLocals[key] = value;
41215 ctrlLocals.$scope.$resolve[key] = value;
41218 // the third param will make the controller instantiate later,private api
41219 // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
41220 ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs);
41221 if (modalOptions.controllerAs && modalOptions.bindToController) {
41222 ctrlInstance = ctrlInstantiate.instance;
41223 ctrlInstance.$close = modalScope.$close;
41224 ctrlInstance.$dismiss = modalScope.$dismiss;
41225 angular.extend(ctrlInstance, {
41226 $resolve: ctrlLocals.$scope.$resolve
41230 ctrlInstance = ctrlInstantiate();
41232 if (angular.isFunction(ctrlInstance.$onInit)) {
41233 ctrlInstance.$onInit();
41235 ctrlLocals.$uibModalInstance = modalInstance;
41236 angular.forEach(tplAndVars[1], function(value, key) {
41237 ctrlLocals[key] = value;
41240 ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
41241 if (modalOptions.controllerAs) {
41242 if (modalOptions.bindToController) {
41243 ctrlInstance.$close = modalScope.$close;
41244 ctrlInstance.$dismiss = modalScope.$dismiss;
41245 angular.extend(ctrlInstance, providedScope);
41248 modalScope[modalOptions.controllerAs] = ctrlInstance;
41249 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41253 $modalStack.open(modalInstance, {
41255 deferred: modalResultDeferred,
41256 renderDeferred: modalRenderDeferred,
41257 closedDeferred: modalClosedDeferred,
41258 content: tplAndVars[0],
41259 animation: modalOptions.animation,
41260 backdrop: modalOptions.backdrop,
41261 keyboard: modalOptions.keyboard,
41262 backdropClass: modalOptions.backdropClass,
41263 windowTopClass: modalOptions.windowTopClass,
41264 windowClass: modalOptions.windowClass,
41265 windowTemplateUrl: modalOptions.windowTemplateUrl,
41266 size: modalOptions.size,
41267 openedClass: modalOptions.openedClass,
41268 appendTo: modalOptions.appendTo
41270 modalOpenedDeferred.resolve(true);
41272 }, function resolveError(reason) {
41273 modalOpenedDeferred.reject(reason);
41274 modalResultDeferred.reject(reason);
41275 })['finally'](function() {
41276 if (promiseChain === samePromise) {
41277 promiseChain = null;
41281 return modalInstance;
41289 return $modalProvider;
41292 angular.module('ui.bootstrap.paging', [])
41294 * Helper internal service for generating common controller code between the
41295 * pager and pagination components
41297 .factory('uibPaging', ['$parse', function($parse) {
41299 create: function(ctrl, $scope, $attrs) {
41300 ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
41301 ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
41303 ctrl._watchers = [];
41305 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41307 ctrl.init = function(ngModelCtrl, config) {
41308 ctrl.ngModelCtrl = ngModelCtrl;
41309 ctrl.config = config;
41311 ngModelCtrl.$render = function() {
41315 if ($attrs.itemsPerPage) {
41317 ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) {
41318 ctrl.itemsPerPage = parseInt(value, 10);
41319 $scope.totalPages = ctrl.calculateTotalPages();
41323 $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
41324 ctrl.itemsPerPage = parseInt(value, 10);
41325 $scope.totalPages = ctrl.calculateTotalPages();
41328 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41330 ctrl.itemsPerPage = config.itemsPerPage;
41333 $scope.$watch('totalItems', function(newTotal, oldTotal) {
41334 if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
41335 $scope.totalPages = ctrl.calculateTotalPages();
41341 ctrl.calculateTotalPages = function() {
41342 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
41343 return Math.max(totalPages || 0, 1);
41346 ctrl.render = function() {
41347 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
41350 $scope.selectPage = function(page, evt) {
41352 evt.preventDefault();
41355 var clickAllowed = !$scope.ngDisabled || !evt;
41356 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
41357 if (evt && evt.target) {
41360 ctrl.ngModelCtrl.$setViewValue(page);
41361 ctrl.ngModelCtrl.$render();
41365 $scope.getText = function(key) {
41366 return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
41369 $scope.noPrevious = function() {
41370 return $scope.page === 1;
41373 $scope.noNext = function() {
41374 return $scope.page === $scope.totalPages;
41377 ctrl.updatePage = function() {
41378 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
41380 if ($scope.page > $scope.totalPages) {
41381 $scope.selectPage($scope.totalPages);
41383 ctrl.ngModelCtrl.$render();
41388 $scope.$on('$destroy', function() {
41389 while (ctrl._watchers.length) {
41390 ctrl._watchers.shift()();
41394 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41399 angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
41401 .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
41402 $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
41404 uibPaging.create(this, $scope, $attrs);
41407 .constant('uibPagerConfig', {
41409 previousText: '« Previous',
41410 nextText: 'Next »',
41414 .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
41422 require: ['uibPager', '?ngModel'],
41423 controller: 'UibPagerController',
41424 controllerAs: 'pager',
41425 templateUrl: function(element, attrs) {
41426 return attrs.templateUrl || 'uib/template/pager/pager.html';
41429 link: function(scope, element, attrs, ctrls) {
41430 var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
41432 if (!ngModelCtrl) {
41433 return; // do nothing if no ng-model
41436 paginationCtrl.init(ngModelCtrl, uibPagerConfig);
41441 angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
41442 .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
41444 // Setup configuration parameters
41445 var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
41446 rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
41447 forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
41449 boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers,
41450 pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity;
41452 boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
41453 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41454 $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
41455 $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
41457 uibPaging.create(this, $scope, $attrs);
41459 if ($attrs.maxSize) {
41461 ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) {
41462 maxSize = parseInt(value, 10);
41466 $scope.$parent.$watch($parse($attrs.maxSize), function(value) {
41467 maxSize = parseInt(value, 10);
41470 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41473 // Create page object used in template
41474 function makePage(number, text, isActive) {
41482 function getPages(currentPage, totalPages) {
41485 // Default page limits
41486 var startPage = 1, endPage = totalPages;
41487 var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
41489 // recompute if maxSize
41492 // Current page is displayed in the middle of the visible ones
41493 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
41494 endPage = startPage + maxSize - 1;
41496 // Adjust if limit is exceeded
41497 if (endPage > totalPages) {
41498 endPage = totalPages;
41499 startPage = endPage - maxSize + 1;
41502 // Visible pages are paginated with maxSize
41503 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
41505 // Adjust last page if limit is exceeded
41506 endPage = Math.min(startPage + maxSize - 1, totalPages);
41510 // Add page number links
41511 for (var number = startPage; number <= endPage; number++) {
41513 var page = makePage(number, pageLabel(number), number === currentPage);
41515 var page = makePage(number, number, number === currentPage);
41516 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41520 // Add links to move between page sets
41521 if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
41522 if (startPage > 1) {
41523 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
41524 var previousPageSet = makePage(startPage - 1, '...', false);
41525 pages.unshift(previousPageSet);
41527 if (boundaryLinkNumbers) {
41528 if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
41529 var secondPageLink = makePage(2, '2', false);
41530 pages.unshift(secondPageLink);
41532 //add the first page
41533 var firstPageLink = makePage(1, '1', false);
41534 pages.unshift(firstPageLink);
41538 if (endPage < totalPages) {
41539 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
41540 var nextPageSet = makePage(endPage + 1, '...', false);
41541 pages.push(nextPageSet);
41543 if (boundaryLinkNumbers) {
41544 if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
41545 var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
41546 pages.push(secondToLastPageLink);
41548 //add the last page
41549 var lastPageLink = makePage(totalPages, totalPages, false);
41550 pages.push(lastPageLink);
41557 var originalRender = this.render;
41558 this.render = function() {
41560 if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
41561 $scope.pages = getPages($scope.page, $scope.totalPages);
41566 .constant('uibPaginationConfig', {
41568 boundaryLinks: false,
41569 boundaryLinkNumbers: false,
41570 directionLinks: true,
41571 firstText: 'First',
41572 previousText: 'Previous',
41576 forceEllipses: false
41579 .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
41589 require: ['uibPagination', '?ngModel'],
41590 controller: 'UibPaginationController',
41591 controllerAs: 'pagination',
41592 templateUrl: function(element, attrs) {
41593 return attrs.templateUrl || 'uib/template/pagination/pagination.html';
41596 link: function(scope, element, attrs, ctrls) {
41597 var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
41599 if (!ngModelCtrl) {
41600 return; // do nothing if no ng-model
41603 paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
41609 * The following features are still outstanding: animation as a
41610 * function, placement as a function, inside, support for more triggers than
41611 * just mouse enter/leave, html tooltips, and selector delegation.
41613 angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
41616 * The $tooltip service creates tooltip- and popover-like directives as well as
41617 * houses global options for them.
41619 .provider('$uibTooltip', function() {
41620 // The default options tooltip and popover.
41621 var defaultOptions = {
41623 placementClassPrefix: '',
41626 popupCloseDelay: 0,
41627 useContentExp: false
41630 // Default hide triggers for each show trigger
41632 'mouseenter': 'mouseleave',
41634 'outsideClick': 'outsideClick',
41639 // The options specified to the provider globally.
41640 var globalOptions = {};
41643 * `options({})` allows global configuration of all tooltips in the
41646 * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
41647 * // place tooltips left instead of top by default
41648 * $tooltipProvider.options( { placement: 'left' } );
41651 this.options = function(value) {
41652 angular.extend(globalOptions, value);
41656 * This allows you to extend the set of trigger mappings available. E.g.:
41659 * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } );
41661 * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
41662 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41664 this.setTriggers = function setTriggers(triggers) {
41665 angular.extend(triggerMap, triggers);
41669 * This is a helper function for translating camel-case to snake_case.
41671 function snake_case(name) {
41672 var regexp = /[A-Z]/g;
41673 var separator = '-';
41674 return name.replace(regexp, function(letter, pos) {
41675 return (pos ? separator : '') + letter.toLowerCase();
41680 * Returns the actual instance of the $tooltip service.
41681 * TODO support multiple triggers
41683 this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
41684 var openedTooltips = $$stackedMap.createNew();
41685 $document.on('keypress', keypressListener);
41687 $rootScope.$on('$destroy', function() {
41688 $document.off('keypress', keypressListener);
41691 function keypressListener(e) {
41692 if (e.which === 27) {
41693 var last = openedTooltips.top();
41695 last.value.close();
41696 openedTooltips.removeTop();
41702 return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
41703 options = angular.extend({}, defaultOptions, globalOptions, options);
41706 * Returns an object of show and hide triggers.
41708 * If a trigger is supplied,
41709 * it is used to show the tooltip; otherwise, it will use the `trigger`
41710 * option passed to the `$tooltipProvider.options` method; else it will
41711 * default to the trigger supplied to this directive factory.
41713 * The hide trigger is based on the show trigger. If the `trigger` option
41714 * was passed to the `$tooltipProvider.options` method, it will use the
41715 * mapped trigger from `triggerMap` or the passed trigger if the map is
41716 * undefined; otherwise, it uses the `triggerMap` value of the show
41717 * trigger; else it will just use the show trigger.
41719 function getTriggers(trigger) {
41720 var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
41721 var hide = show.map(function(trigger) {
41722 return triggerMap[trigger] || trigger;
41730 var directiveName = snake_case(ttType);
41732 var startSym = $interpolate.startSymbol();
41733 var endSym = $interpolate.endSymbol();
41736 '<div '+ directiveName + '-popup ' +
41737 'uib-title="' + startSym + 'title' + endSym + '" ' +
41738 (options.useContentExp ?
41739 'content-exp="contentExp()" ' :
41740 'content="' + startSym + 'content' + endSym + '" ') +
41741 'placement="' + startSym + 'placement' + endSym + '" ' +
41742 'popup-class="' + startSym + 'popupClass' + endSym + '" ' +
41743 'animation="animation" ' +
41744 'is-open="isOpen" ' +
41745 'origin-scope="origScope" ' +
41746 'class="uib-position-measure"' +
41748 '<div '+ directiveName + '-popup '+
41749 'title="' + startSym + 'title' + endSym + '" '+
41750 (options.useContentExp ?
41751 'content-exp="contentExp()" ' :
41752 'content="' + startSym + 'content' + endSym + '" ') +
41753 'placement="' + startSym + 'placement' + endSym + '" '+
41754 'popup-class="' + startSym + 'popupClass' + endSym + '" '+
41755 'animation="animation" ' +
41756 'is-open="isOpen"' +
41757 'origin-scope="origScope" ' +
41758 'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
41759 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41764 compile: function(tElem, tAttrs) {
41765 var tooltipLinker = $compile(template);
41767 return function link(scope, element, attrs, tooltipCtrl) {
41769 var tooltipLinkedScope;
41770 var transitionTimeout;
41773 var positionTimeout;
41774 var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
41775 var triggers = getTriggers(undefined);
41776 var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
41777 var ttScope = scope.$new(true);
41778 var repositionScheduled = false;
41779 var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
41780 var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
41781 var observers = [];
41785 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41787 var positionTooltip = function() {
41788 // check if tooltip exists and is not empty
41789 if (!tooltip || !tooltip.html()) { return; }
41791 if (!positionTimeout) {
41792 positionTimeout = $timeout(function() {
41794 var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
41795 tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' });
41797 if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) {
41798 tooltip.removeClass(lastPlacement.split('-')[0]);
41799 tooltip.addClass(ttPosition.placement.split('-')[0]);
41802 if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) {
41803 tooltip.removeClass(options.placementClassPrefix + lastPlacement);
41804 tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
41807 // first time through tt element will have the
41808 // uib-position-measure class or if the placement
41809 // has changed we need to position the arrow.
41810 if (tooltip.hasClass('uib-position-measure')) {
41811 $position.positionArrow(tooltip, ttPosition.placement);
41812 tooltip.removeClass('uib-position-measure');
41813 } else if (lastPlacement !== ttPosition.placement) {
41814 $position.positionArrow(tooltip, ttPosition.placement);
41816 lastPlacement = ttPosition.placement;
41818 // Reset the positioning.
41819 tooltip.css({ top: 0, left: 0 });
41821 // Now set the calculated positioning.
41822 var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
41823 tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' });
41825 // If the placement class is prefixed, still need
41826 // to remove the TWBS standard class.
41827 if (options.placementClassPrefix) {
41828 tooltip.removeClass('top bottom left right');
41831 tooltip.removeClass(
41832 options.placementClassPrefix + 'top ' +
41833 options.placementClassPrefix + 'top-left ' +
41834 options.placementClassPrefix + 'top-right ' +
41835 options.placementClassPrefix + 'bottom ' +
41836 options.placementClassPrefix + 'bottom-left ' +
41837 options.placementClassPrefix + 'bottom-right ' +
41838 options.placementClassPrefix + 'left ' +
41839 options.placementClassPrefix + 'left-top ' +
41840 options.placementClassPrefix + 'left-bottom ' +
41841 options.placementClassPrefix + 'right ' +
41842 options.placementClassPrefix + 'right-top ' +
41843 options.placementClassPrefix + 'right-bottom');
41845 var placement = ttPosition.placement.split('-');
41846 tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement);
41847 $position.positionArrow(tooltip, ttPosition.placement);
41848 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41850 positionTimeout = null;
41855 // Set up the correct scope to allow transclusion later
41856 ttScope.origScope = scope;
41858 // By default, the tooltip is not open.
41859 // TODO add ability to start tooltip opened
41860 ttScope.isOpen = false;
41861 openedTooltips.add(ttScope, {
41865 function toggleTooltipBind() {
41866 if (!ttScope.isOpen) {
41873 // Show the tooltip with delay if specified, otherwise show it immediately
41874 function showTooltipBind() {
41875 if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
41882 if (ttScope.popupDelay) {
41883 // Do nothing if the tooltip was already scheduled to pop-up.
41884 // This happens if show is triggered multiple times before any hide is triggered.
41885 if (!showTimeout) {
41886 showTimeout = $timeout(show, ttScope.popupDelay, false);
41893 function hideTooltipBind() {
41896 if (ttScope.popupCloseDelay) {
41897 if (!hideTimeout) {
41898 hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
41905 // Show the tooltip popup element.
41910 // Don't show empty tooltips.
41911 if (!ttScope.content) {
41912 return angular.noop;
41917 // And show the tooltip.
41918 ttScope.$evalAsync(function() {
41919 ttScope.isOpen = true;
41920 assignIsOpen(true);
41925 function cancelShow() {
41927 $timeout.cancel(showTimeout);
41928 showTimeout = null;
41931 if (positionTimeout) {
41932 $timeout.cancel(positionTimeout);
41933 positionTimeout = null;
41937 // Hide the tooltip popup element.
41943 // First things first: we don't show it anymore.
41944 ttScope.$evalAsync(function() {
41947 ttScope.isOpen = false;
41948 assignIsOpen(false);
41949 // And now we remove it from the DOM. However, if we have animation, we
41950 // need to wait for it to expire beforehand.
41951 // FIXME: this is a placeholder for a port of the transitions library.
41952 // The fade transition in TWBS is 150ms.
41953 if (ttScope.animation) {
41954 if (!transitionTimeout) {
41955 transitionTimeout = $timeout(removeTooltip, 150, false);
41961 ttScope.isOpen = false;
41962 assignIsOpen(false);
41963 // And now we remove it from the DOM. However, if we have animation, we
41964 // need to wait for it to expire beforehand.
41965 // FIXME: this is a placeholder for a port of the transitions library.
41966 // The fade transition in TWBS is 150ms.
41967 if (ttScope.animation) {
41968 if (!transitionTimeout) {
41969 transitionTimeout = $timeout(removeTooltip, 150, false);
41973 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41978 function cancelHide() {
41980 $timeout.cancel(hideTimeout);
41981 hideTimeout = null;
41986 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
41987 if (transitionTimeout) {
41988 $timeout.cancel(transitionTimeout);
41989 transitionTimeout = null;
41993 function createTooltip() {
41994 // There can only be one tooltip element per directive shown at once.
41999 tooltipLinkedScope = ttScope.$new();
42000 tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
42001 if (appendToBody) {
42002 $document.find('body').append(tooltip);
42004 element.after(tooltip);
42011 function removeTooltip() {
42014 unregisterObservers();
42020 if (tooltipLinkedScope) {
42021 tooltipLinkedScope.$destroy();
42022 tooltipLinkedScope = null;
42027 * Set the initial scope values. Once
42028 * the tooltip is created, the observers
42029 * will be added to keep things in sync.
42031 function prepareTooltip() {
42032 ttScope.title = attrs[prefix + 'Title'];
42033 if (contentParse) {
42034 ttScope.content = contentParse(scope);
42036 ttScope.content = attrs[ttType];
42039 ttScope.popupClass = attrs[prefix + 'Class'];
42040 ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
42042 var placement = $position.parsePlacement(ttScope.placement);
42043 lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0];
42045 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42047 var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
42048 var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
42049 ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
42050 ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
42053 function assignIsOpen(isOpen) {
42054 if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
42055 isOpenParse.assign(scope, isOpen);
42059 ttScope.contentExp = function() {
42060 return ttScope.content;
42064 * Observe the relevant attributes.
42066 attrs.$observe('disabled', function(val) {
42071 if (val && ttScope.isOpen) {
42077 scope.$watch(isOpenParse, function(val) {
42078 if (ttScope && !val === ttScope.isOpen) {
42079 toggleTooltipBind();
42084 function prepObservers() {
42085 observers.length = 0;
42087 if (contentParse) {
42089 scope.$watch(contentParse, function(val) {
42090 ttScope.content = val;
42091 if (!val && ttScope.isOpen) {
42098 tooltipLinkedScope.$watch(function() {
42099 if (!repositionScheduled) {
42100 repositionScheduled = true;
42101 tooltipLinkedScope.$$postDigest(function() {
42102 repositionScheduled = false;
42103 if (ttScope && ttScope.isOpen) {
42112 attrs.$observe(ttType, function(val) {
42113 ttScope.content = val;
42114 if (!val && ttScope.isOpen) {
42124 attrs.$observe(prefix + 'Title', function(val) {
42125 ttScope.title = val;
42126 if (ttScope.isOpen) {
42133 attrs.$observe(prefix + 'Placement', function(val) {
42134 ttScope.placement = val ? val : options.placement;
42135 if (ttScope.isOpen) {
42142 function unregisterObservers() {
42143 if (observers.length) {
42144 angular.forEach(observers, function(observer) {
42147 observers.length = 0;
42151 // hide tooltips/popovers for outsideClick trigger
42152 function bodyHideTooltipBind(e) {
42153 if (!ttScope || !ttScope.isOpen || !tooltip) {
42156 // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
42157 if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
42162 var unregisterTriggers = function() {
42163 triggers.show.forEach(function(trigger) {
42164 if (trigger === 'outsideClick') {
42165 element.off('click', toggleTooltipBind);
42167 element.off(trigger, showTooltipBind);
42168 element.off(trigger, toggleTooltipBind);
42171 triggers.hide.forEach(function(trigger) {
42172 if (trigger === 'outsideClick') {
42173 $document.off('click', bodyHideTooltipBind);
42175 element.off(trigger, hideTooltipBind);
42180 function prepTriggers() {
42181 var val = attrs[prefix + 'Trigger'];
42182 unregisterTriggers();
42184 triggers = getTriggers(val);
42186 if (triggers.show !== 'none') {
42187 triggers.show.forEach(function(trigger, idx) {
42188 if (trigger === 'outsideClick') {
42189 element.on('click', toggleTooltipBind);
42190 $document.on('click', bodyHideTooltipBind);
42191 } else if (trigger === triggers.hide[idx]) {
42192 element.on(trigger, toggleTooltipBind);
42193 } else if (trigger) {
42194 element.on(trigger, showTooltipBind);
42195 element.on(triggers.hide[idx], hideTooltipBind);
42198 element.on('keypress', function(e) {
42199 if (e.which === 27) {
42209 var animation = scope.$eval(attrs[prefix + 'Animation']);
42210 ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
42212 var appendToBodyVal;
42213 var appendKey = prefix + 'AppendToBody';
42214 if (appendKey in attrs && attrs[appendKey] === undefined) {
42215 appendToBodyVal = true;
42217 appendToBodyVal = scope.$eval(attrs[appendKey]);
42220 appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
42224 // if a tooltip is attached to <body> we need to remove it on
42225 // location change as its parent scope will probably not be destroyed
42227 if (appendToBody) {
42228 scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
42229 if (ttScope.isOpen) {
42235 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42236 // Make sure tooltip is destroyed and removed.
42237 scope.$on('$destroy', function onDestroyTooltip() {
42238 unregisterTriggers();
42240 openedTooltips.remove(ttScope);
42250 // This is mostly ngInclude code but with a custom scope
42251 .directive('uibTooltipTemplateTransclude', [
42252 '$animate', '$sce', '$compile', '$templateRequest',
42253 function ($animate, $sce, $compile, $templateRequest) {
42255 link: function(scope, elem, attrs) {
42256 var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
42258 var changeCounter = 0,
42263 var cleanupLastIncludeContent = function() {
42264 if (previousElement) {
42265 previousElement.remove();
42266 previousElement = null;
42269 if (currentScope) {
42270 currentScope.$destroy();
42271 currentScope = null;
42274 if (currentElement) {
42275 $animate.leave(currentElement).then(function() {
42276 previousElement = null;
42278 previousElement = currentElement;
42279 currentElement = null;
42283 scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
42284 var thisChangeId = ++changeCounter;
42287 //set the 2nd param to true to ignore the template request error so that the inner
42288 //contents and scope can be cleaned up.
42289 $templateRequest(src, true).then(function(response) {
42290 if (thisChangeId !== changeCounter) { return; }
42291 var newScope = origScope.$new();
42292 var template = response;
42294 var clone = $compile(template)(newScope, function(clone) {
42295 cleanupLastIncludeContent();
42296 $animate.enter(clone, elem);
42299 currentScope = newScope;
42300 currentElement = clone;
42302 currentScope.$emit('$includeContentLoaded', src);
42304 if (thisChangeId === changeCounter) {
42305 cleanupLastIncludeContent();
42306 scope.$emit('$includeContentError', src);
42309 scope.$emit('$includeContentRequested', src);
42311 cleanupLastIncludeContent();
42315 scope.$on('$destroy', cleanupLastIncludeContent);
42321 * Note that it's intentional that these classes are *not* applied through $animate.
42322 * They must not be animated as they're expected to be present on the tooltip on
42325 .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
42328 link: function(scope, element, attrs) {
42329 // need to set the primary position so the
42330 // arrow has space during position measure.
42331 // tooltip.positionTooltip()
42332 if (scope.placement) {
42333 // // There are no top-left etc... classes
42334 // // in TWBS, so we need the primary position.
42335 var position = $uibPosition.parsePlacement(scope.placement);
42336 element.addClass(position[0]);
42340 element.addClass('top');
42341 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42344 if (scope.popupClass) {
42345 element.addClass(scope.popupClass);
42348 if (scope.animation()) {
42349 element.addClass(attrs.tooltipAnimationClass);
42355 .directive('uibTooltipPopup', function() {
42358 scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42359 templateUrl: 'uib/template/tooltip/tooltip-popup.html'
42363 .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
42364 return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
42367 .directive('uibTooltipTemplatePopup', function() {
42370 scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
42371 originScope: '&' },
42372 templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
42376 .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
42377 return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
42378 useContentExp: true
42382 .directive('uibTooltipHtmlPopup', function() {
42385 scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42386 templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
42390 .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
42391 return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
42392 useContentExp: true
42397 * The following features are still outstanding: popup delay, animation as a
42398 * function, placement as a function, inside, support for more triggers than
42399 * just mouse enter/leave, and selector delegatation.
42401 angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
42403 .directive('uibPopoverTemplatePopup', function() {
42407 scope: { uibTitle: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
42409 scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
42410 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42411 originScope: '&' },
42412 templateUrl: 'uib/template/popover/popover-template.html'
42416 .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
42417 return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
42418 useContentExp: true
42422 .directive('uibPopoverHtmlPopup', function() {
42426 scope: { contentExp: '&', uibTitle: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42428 scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42429 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42430 templateUrl: 'uib/template/popover/popover-html.html'
42434 .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
42435 return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
42436 useContentExp: true
42440 .directive('uibPopoverPopup', function() {
42444 scope: { uibTitle: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42446 scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
42447 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42448 templateUrl: 'uib/template/popover/popover.html'
42452 .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
42453 return $uibTooltip('uibPopover', 'popover', 'click');
42456 angular.module('ui.bootstrap.progressbar', [])
42458 .constant('uibProgressConfig', {
42463 .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
42465 animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
42469 $scope.max = getMaxOrDefault();
42471 $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
42472 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42474 this.addBar = function(bar, element, attrs) {
42476 element.css({'transition': 'none'});
42479 this.bars.push(bar);
42482 bar.max = getMaxOrDefault();
42484 bar.max = $scope.max;
42485 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42486 bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
42488 bar.$watch('value', function(value) {
42489 bar.recalculatePercentage();
42492 bar.recalculatePercentage = function() {
42493 var totalPercentage = self.bars.reduce(function(total, bar) {
42494 bar.percent = +(100 * bar.value / bar.max).toFixed(2);
42495 return total + bar.percent;
42498 if (totalPercentage > 100) {
42499 bar.percent -= totalPercentage - 100;
42503 bar.$on('$destroy', function() {
42505 self.removeBar(bar);
42509 this.removeBar = function(bar) {
42510 this.bars.splice(this.bars.indexOf(bar), 1);
42511 this.bars.forEach(function (bar) {
42512 bar.recalculatePercentage();
42517 //$attrs.$observe('maxParam', function(maxParam) {
42518 $scope.$watch('maxParam', function(maxParam) {
42519 self.bars.forEach(function(bar) {
42520 bar.max = getMaxOrDefault();
42521 bar.recalculatePercentage();
42525 function getMaxOrDefault () {
42526 return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max;
42529 $scope.$watch('max', function(max) {
42530 self.bars.forEach(function(bar) {
42531 bar.max = $scope.max;
42532 bar.recalculatePercentage();
42535 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42538 .directive('uibProgress', function() {
42542 controller: 'UibProgressController',
42543 require: 'uibProgress',
42549 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42551 templateUrl: 'uib/template/progressbar/progress.html'
42555 .directive('uibBar', function() {
42559 require: '^uibProgress',
42564 templateUrl: 'uib/template/progressbar/bar.html',
42565 link: function(scope, element, attrs, progressCtrl) {
42566 progressCtrl.addBar(scope, element, attrs);
42571 .directive('uibProgressbar', function() {
42575 controller: 'UibProgressController',
42582 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42585 templateUrl: 'uib/template/progressbar/progressbar.html',
42586 link: function(scope, element, attrs, progressCtrl) {
42587 progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
42592 angular.module('ui.bootstrap.rating', [])
42594 .constant('uibRatingConfig', {
42601 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42602 titles : ['one', 'two', 'three', 'four', 'five']
42605 .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
42607 var ngModelCtrl = { $setViewValue: angular.noop },
42610 var ngModelCtrl = { $setViewValue: angular.noop };
42611 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42613 this.init = function(ngModelCtrl_) {
42614 ngModelCtrl = ngModelCtrl_;
42615 ngModelCtrl.$render = this.render;
42617 ngModelCtrl.$formatters.push(function(value) {
42618 if (angular.isNumber(value) && value << 0 !== value) {
42619 value = Math.round(value);
42625 this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
42626 this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
42628 this.enableReset = angular.isDefined($attrs.enableReset) ?
42629 $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset;
42630 var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles;
42632 var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
42633 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42634 this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
42635 tmpTitles : ratingConfig.titles;
42637 var ratingStates = angular.isDefined($attrs.ratingStates) ?
42638 $scope.$parent.$eval($attrs.ratingStates) :
42639 new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
42640 $scope.range = this.buildTemplateObjects(ratingStates);
42643 this.buildTemplateObjects = function(states) {
42644 for (var i = 0, n = states.length; i < n; i++) {
42645 states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
42650 this.getTitle = function(index) {
42651 if (index >= this.titles.length) {
42655 return this.titles[index];
42658 $scope.rate = function(value) {
42659 if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
42661 var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value;
42662 ngModelCtrl.$setViewValue(newViewValue);
42664 ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
42665 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42666 ngModelCtrl.$render();
42670 $scope.enter = function(value) {
42671 if (!$scope.readonly) {
42672 $scope.value = value;
42674 $scope.onHover({value: value});
42677 $scope.reset = function() {
42678 $scope.value = ngModelCtrl.$viewValue;
42682 $scope.onKeydown = function(evt) {
42683 if (/(37|38|39|40)/.test(evt.which)) {
42684 evt.preventDefault();
42685 evt.stopPropagation();
42686 $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
42690 this.render = function() {
42691 $scope.value = ngModelCtrl.$viewValue;
42693 $scope.title = self.getTitle($scope.value - 1);
42695 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42699 .directive('uibRating', function() {
42701 require: ['uibRating', 'ngModel'],
42704 readonly: '=?readOnly',
42707 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42711 controller: 'UibRatingController',
42712 templateUrl: 'uib/template/rating/rating.html',
42714 link: function(scope, element, attrs, ctrls) {
42715 var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
42716 ratingCtrl.init(ngModelCtrl);
42721 angular.module('ui.bootstrap.tabs', [])
42723 .controller('UibTabsetController', ['$scope', function ($scope) {
42729 ctrl.select = function(index, evt) {
42731 var previousIndex = findTabIndex(oldIndex);
42732 var previousSelected = ctrl.tabs[previousIndex];
42733 if (previousSelected) {
42734 previousSelected.tab.onDeselect({
42736 $selectedIndex: index
42738 if (evt && evt.isDefaultPrevented()) {
42741 previousSelected.tab.active = false;
42744 var selected = ctrl.tabs[index];
42746 selected.tab.onSelect({
42749 selected.tab.active = true;
42750 ctrl.active = selected.index;
42751 oldIndex = selected.index;
42752 } else if (!selected && angular.isDefined(oldIndex)) {
42753 ctrl.active = null;
42757 tabs = ctrl.tabs = $scope.tabs = [];
42759 ctrl.select = function(selectedTab) {
42760 angular.forEach(tabs, function(tab) {
42761 if (tab.active && tab !== selectedTab) {
42762 tab.active = false;
42764 selectedTab.selectCalled = false;
42767 selectedTab.active = true;
42768 // only call select if it has not already been called
42769 if (!selectedTab.selectCalled) {
42770 selectedTab.onSelect();
42771 selectedTab.selectCalled = true;
42772 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42776 ctrl.addTab = function addTab(tab) {
42782 ctrl.tabs.sort(function(t1, t2) {
42783 if (t1.index > t2.index) {
42787 if (t1.index < t2.index) {
42794 if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) {
42795 var newActiveIndex = findTabIndex(tab.index);
42796 ctrl.select(newActiveIndex);
42799 // we can't run the select function on the first tab
42800 // since that would select it twice
42801 if (tabs.length === 1 && tab.active !== false) {
42803 } else if (tab.active) {
42806 tab.active = false;
42807 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42811 ctrl.removeTab = function removeTab(tab) {
42814 for (var i = 0; i < ctrl.tabs.length; i++) {
42815 if (ctrl.tabs[i].tab === tab) {
42821 if (ctrl.tabs[index].index === ctrl.active) {
42822 var newActiveTabIndex = index === ctrl.tabs.length - 1 ?
42823 index - 1 : index + 1 % ctrl.tabs.length;
42824 ctrl.select(newActiveTabIndex);
42827 ctrl.tabs.splice(index, 1);
42830 $scope.$watch('tabset.active', function(val) {
42831 if (angular.isDefined(val) && val !== oldIndex) {
42832 ctrl.select(findTabIndex(val));
42837 var index = tabs.indexOf(tab);
42838 //Select a new tab if the tab to be removed is selected and not destroyed
42839 if (tab.active && tabs.length > 1 && !destroyed) {
42840 //If this is the last tab, select the previous tab. else, the next tab.
42841 var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
42842 ctrl.select(tabs[newActiveIndex]);
42844 tabs.splice(index, 1);
42847 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42849 $scope.$on('$destroy', function() {
42854 function findTabIndex(index) {
42855 for (var i = 0; i < ctrl.tabs.length; i++) {
42856 if (ctrl.tabs[i].index === index) {
42862 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42865 .directive('uibTabset', function() {
42871 bindToController: {
42875 controller: 'UibTabsetController',
42876 controllerAs: 'tabset',
42877 templateUrl: function(element, attrs) {
42878 return attrs.templateUrl || 'uib/template/tabs/tabset.html';
42880 link: function(scope, element, attrs) {
42881 scope.vertical = angular.isDefined(attrs.vertical) ?
42882 scope.$parent.$eval(attrs.vertical) : false;
42883 scope.justified = angular.isDefined(attrs.justified) ?
42884 scope.$parent.$eval(attrs.justified) : false;
42889 controller: 'UibTabsetController',
42890 templateUrl: 'uib/template/tabs/tabset.html',
42891 link: function(scope, element, attrs) {
42892 scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
42893 scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
42894 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42899 .directive('uibTab', ['$parse', function($parse) {
42901 require: '^uibTabset',
42904 templateUrl: function(element, attrs) {
42905 return attrs.templateUrl || 'uib/template/tabs/tab.html';
42913 templateUrl: 'uib/template/tabs/tab.html',
42918 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42919 onSelect: '&select', //This callback is called in contentHeadingTransclude
42920 //once it inserts the tab's content into the dom
42921 onDeselect: '&deselect'
42923 controller: function() {
42924 //Empty controller so other directives can require being 'under' a tab
42926 controllerAs: 'tab',
42927 link: function(scope, elm, attrs, tabsetCtrl, transclude) {
42930 scope.$watch('active', function(active) {
42932 tabsetCtrl.select(scope);
42936 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42937 scope.disabled = false;
42938 if (attrs.disable) {
42939 scope.$parent.$watch($parse(attrs.disable), function(value) {
42940 scope.disabled = !! value;
42945 if (angular.isUndefined(attrs.index)) {
42946 if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) {
42947 scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1;
42953 if (angular.isUndefined(attrs.classes)) {
42954 scope.classes = '';
42957 scope.select = function(evt) {
42958 if (!scope.disabled) {
42960 for (var i = 0; i < tabsetCtrl.tabs.length; i++) {
42961 if (tabsetCtrl.tabs[i].tab === scope) {
42967 tabsetCtrl.select(index, evt);
42969 scope.select = function() {
42970 if (!scope.disabled) {
42971 scope.active = true;
42972 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
42976 tabsetCtrl.addTab(scope);
42977 scope.$on('$destroy', function() {
42978 tabsetCtrl.removeTab(scope);
42981 //We need to transclude later, once the content container is ready.
42982 //when this link happens, we're inside a tab heading.
42983 scope.$transcludeFn = transclude;
42988 .directive('uibTabHeadingTransclude', function() {
42991 require: '^uibTab',
42992 link: function(scope, elm) {
42993 scope.$watch('headingElement', function updateHeadingElement(heading) {
42996 elm.append(heading);
43003 .directive('uibTabContentTransclude', function() {
43006 require: '^uibTabset',
43007 link: function(scope, elm, attrs) {
43009 var tab = scope.$eval(attrs.uibTabContentTransclude).tab;
43011 var tab = scope.$eval(attrs.uibTabContentTransclude);
43012 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43014 //Now our tab is ready to be transcluded: both the tab heading area
43015 //and the tab content area are loaded. Transclude 'em both.
43016 tab.$transcludeFn(tab.$parent, function(contents) {
43017 angular.forEach(contents, function(node) {
43018 if (isTabHeading(node)) {
43019 //Let tabHeadingTransclude know.
43020 tab.headingElement = node;
43029 function isTabHeading(node) {
43030 return node.tagName && (
43031 node.hasAttribute('uib-tab-heading') ||
43032 node.hasAttribute('data-uib-tab-heading') ||
43033 node.hasAttribute('x-uib-tab-heading') ||
43034 node.tagName.toLowerCase() === 'uib-tab-heading' ||
43035 node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
43037 node.tagName.toLowerCase() === 'x-uib-tab-heading' ||
43038 node.tagName.toLowerCase() === 'uib:tab-heading'
43040 node.tagName.toLowerCase() === 'x-uib-tab-heading'
43041 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43046 angular.module('ui.bootstrap.timepicker', [])
43048 .constant('uibTimepickerConfig', {
43052 showMeridian: true,
43053 showSeconds: false,
43055 readonlyInput: false,
43059 showSpinners: true,
43060 templateUrl: 'uib/template/timepicker/timepicker.html'
43063 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43066 .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
43067 var selected = new Date(),
43070 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
43071 meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS,
43072 padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true;
43074 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
43075 meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
43076 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43078 $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
43079 $element.removeAttr('tabindex');
43081 this.init = function(ngModelCtrl_, inputs) {
43082 ngModelCtrl = ngModelCtrl_;
43083 ngModelCtrl.$render = this.render;
43085 ngModelCtrl.$formatters.unshift(function(modelValue) {
43086 return modelValue ? new Date(modelValue) : null;
43089 var hoursInputEl = inputs.eq(0),
43090 minutesInputEl = inputs.eq(1),
43091 secondsInputEl = inputs.eq(2);
43093 var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
43096 this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
43099 var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
43101 this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
43104 $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
43105 this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
43108 var hourStep = timepickerConfig.hourStep;
43109 if ($attrs.hourStep) {
43111 watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) {
43115 $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
43116 hourStep = parseInt(value, 10);
43118 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43121 var minuteStep = timepickerConfig.minuteStep;
43122 if ($attrs.minuteStep) {
43124 watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
43125 minuteStep = +value;
43130 watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) {
43131 var dt = new Date(value);
43132 min = isNaN(dt) ? undefined : dt;
43136 watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) {
43137 var dt = new Date(value);
43138 max = isNaN(dt) ? undefined : dt;
43141 var disabled = false;
43142 if ($attrs.ngDisabled) {
43143 watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
43147 $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
43148 minuteStep = parseInt(value, 10);
43153 $scope.$parent.$watch($parse($attrs.min), function(value) {
43154 var dt = new Date(value);
43155 min = isNaN(dt) ? undefined : dt;
43159 $scope.$parent.$watch($parse($attrs.max), function(value) {
43160 var dt = new Date(value);
43161 max = isNaN(dt) ? undefined : dt;
43164 var disabled = false;
43165 if ($attrs.ngDisabled) {
43166 $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
43169 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43172 $scope.noIncrementHours = function() {
43173 var incrementedSelected = addMinutes(selected, hourStep * 60);
43174 return disabled || incrementedSelected > max ||
43175 incrementedSelected < selected && incrementedSelected < min;
43178 $scope.noDecrementHours = function() {
43179 var decrementedSelected = addMinutes(selected, -hourStep * 60);
43180 return disabled || decrementedSelected < min ||
43181 decrementedSelected > selected && decrementedSelected > max;
43184 $scope.noIncrementMinutes = function() {
43185 var incrementedSelected = addMinutes(selected, minuteStep);
43186 return disabled || incrementedSelected > max ||
43187 incrementedSelected < selected && incrementedSelected < min;
43190 $scope.noDecrementMinutes = function() {
43191 var decrementedSelected = addMinutes(selected, -minuteStep);
43192 return disabled || decrementedSelected < min ||
43193 decrementedSelected > selected && decrementedSelected > max;
43196 $scope.noIncrementSeconds = function() {
43197 var incrementedSelected = addSeconds(selected, secondStep);
43198 return disabled || incrementedSelected > max ||
43199 incrementedSelected < selected && incrementedSelected < min;
43202 $scope.noDecrementSeconds = function() {
43203 var decrementedSelected = addSeconds(selected, -secondStep);
43204 return disabled || decrementedSelected < min ||
43205 decrementedSelected > selected && decrementedSelected > max;
43208 $scope.noToggleMeridian = function() {
43209 if (selected.getHours() < 12) {
43210 return disabled || addMinutes(selected, 12 * 60) > max;
43213 return disabled || addMinutes(selected, -12 * 60) < min;
43216 var secondStep = timepickerConfig.secondStep;
43217 if ($attrs.secondStep) {
43219 watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) {
43220 secondStep = +value;
43223 $scope.$parent.$watch($parse($attrs.secondStep), function(value) {
43224 secondStep = parseInt(value, 10);
43226 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43229 $scope.showSeconds = timepickerConfig.showSeconds;
43230 if ($attrs.showSeconds) {
43232 watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
43233 $scope.showSeconds = !!value;
43236 $scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
43237 $scope.showSeconds = !!value;
43239 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43243 $scope.showMeridian = timepickerConfig.showMeridian;
43244 if ($attrs.showMeridian) {
43246 watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
43248 $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
43249 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43250 $scope.showMeridian = !!value;
43252 if (ngModelCtrl.$error.time) {
43253 // Evaluate from template
43254 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
43255 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
43256 selected.setHours(hours);
43266 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43269 // Get $scope.hours in 24H mode if valid
43270 function getHoursFromTemplate() {
43272 var hours = +$scope.hours;
43273 var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
43274 hours >= 0 && hours < 24;
43275 if (!valid || $scope.hours === '') {
43277 var hours = parseInt($scope.hours, 10);
43278 var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
43279 hours >= 0 && hours < 24;
43281 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43285 if ($scope.showMeridian) {
43286 if (hours === 12) {
43289 if ($scope.meridian === meridians[1]) {
43290 hours = hours + 12;
43296 function getMinutesFromTemplate() {
43298 var minutes = +$scope.minutes;
43299 var valid = minutes >= 0 && minutes < 60;
43300 if (!valid || $scope.minutes === '') {
43306 function getSecondsFromTemplate() {
43307 var seconds = +$scope.seconds;
43308 return seconds >= 0 && seconds < 60 ? seconds : undefined;
43311 function pad(value, noPad) {
43313 var minutes = parseInt($scope.minutes, 10);
43314 return minutes >= 0 && minutes < 60 ? minutes : undefined;
43317 function getSecondsFromTemplate() {
43318 var seconds = parseInt($scope.seconds, 10);
43319 return seconds >= 0 && seconds < 60 ? seconds : undefined;
43322 function pad(value) {
43323 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43324 if (value === null) {
43329 return angular.isDefined(value) && value.toString().length < 2 && !noPad ?
43331 return angular.isDefined(value) && value.toString().length < 2 ?
43332 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43333 '0' + value : value.toString();
43336 // Respond on mousewheel spin
43337 this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
43338 var isScrollingUp = function(e) {
43339 if (e.originalEvent) {
43340 e = e.originalEvent;
43342 //pick correct delta variable depending on event
43343 var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
43344 return e.detail || delta > 0;
43347 hoursInputEl.bind('mousewheel wheel', function(e) {
43349 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
43351 e.preventDefault();
43354 minutesInputEl.bind('mousewheel wheel', function(e) {
43356 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
43358 e.preventDefault();
43361 secondsInputEl.bind('mousewheel wheel', function(e) {
43363 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
43365 e.preventDefault();
43369 // Respond on up/down arrowkeys
43370 this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
43371 hoursInputEl.bind('keydown', function(e) {
43373 if (e.which === 38) { // up
43374 e.preventDefault();
43375 $scope.incrementHours();
43377 } else if (e.which === 40) { // down
43378 e.preventDefault();
43379 $scope.decrementHours();
43385 minutesInputEl.bind('keydown', function(e) {
43387 if (e.which === 38) { // up
43388 e.preventDefault();
43389 $scope.incrementMinutes();
43391 } else if (e.which === 40) { // down
43392 e.preventDefault();
43393 $scope.decrementMinutes();
43399 secondsInputEl.bind('keydown', function(e) {
43401 if (e.which === 38) { // up
43402 e.preventDefault();
43403 $scope.incrementSeconds();
43405 } else if (e.which === 40) { // down
43406 e.preventDefault();
43407 $scope.decrementSeconds();
43414 this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
43415 if ($scope.readonlyInput) {
43416 $scope.updateHours = angular.noop;
43417 $scope.updateMinutes = angular.noop;
43418 $scope.updateSeconds = angular.noop;
43422 var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
43423 ngModelCtrl.$setViewValue(null);
43424 ngModelCtrl.$setValidity('time', false);
43425 if (angular.isDefined(invalidHours)) {
43426 $scope.invalidHours = invalidHours;
43429 if (angular.isDefined(invalidMinutes)) {
43430 $scope.invalidMinutes = invalidMinutes;
43433 if (angular.isDefined(invalidSeconds)) {
43434 $scope.invalidSeconds = invalidSeconds;
43438 $scope.updateHours = function() {
43439 var hours = getHoursFromTemplate(),
43440 minutes = getMinutesFromTemplate();
43442 ngModelCtrl.$setDirty();
43444 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
43445 selected.setHours(hours);
43446 selected.setMinutes(minutes);
43447 if (selected < min || selected > max) {
43457 hoursInputEl.bind('blur', function(e) {
43458 ngModelCtrl.$setTouched();
43460 if (modelIsEmpty()) {
43462 } else if ($scope.hours === null || $scope.hours === '') {
43464 } else if (!$scope.invalidHours && $scope.hours < 10) {
43465 $scope.$apply(function() {
43466 $scope.hours = pad($scope.hours, !padHours);
43468 if ($scope.hours === null || $scope.hours === '') {
43470 } else if (!$scope.invalidHours && $scope.hours < 10) {
43471 $scope.$apply(function() {
43472 $scope.hours = pad($scope.hours);
43473 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43478 $scope.updateMinutes = function() {
43479 var minutes = getMinutesFromTemplate(),
43480 hours = getHoursFromTemplate();
43482 ngModelCtrl.$setDirty();
43484 if (angular.isDefined(minutes) && angular.isDefined(hours)) {
43485 selected.setHours(hours);
43486 selected.setMinutes(minutes);
43487 if (selected < min || selected > max) {
43488 invalidate(undefined, true);
43493 invalidate(undefined, true);
43497 minutesInputEl.bind('blur', function(e) {
43498 ngModelCtrl.$setTouched();
43500 if (modelIsEmpty()) {
43502 } else if ($scope.minutes === null) {
43504 if ($scope.minutes === null) {
43505 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43506 invalidate(undefined, true);
43507 } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
43508 $scope.$apply(function() {
43509 $scope.minutes = pad($scope.minutes);
43514 $scope.updateSeconds = function() {
43515 var seconds = getSecondsFromTemplate();
43517 ngModelCtrl.$setDirty();
43519 if (angular.isDefined(seconds)) {
43520 selected.setSeconds(seconds);
43523 invalidate(undefined, undefined, true);
43527 secondsInputEl.bind('blur', function(e) {
43529 if (modelIsEmpty()) {
43531 } else if (!$scope.invalidSeconds && $scope.seconds < 10) {
43533 if (!$scope.invalidSeconds && $scope.seconds < 10) {
43534 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43535 $scope.$apply( function() {
43536 $scope.seconds = pad($scope.seconds);
43543 this.render = function() {
43544 var date = ngModelCtrl.$viewValue;
43547 ngModelCtrl.$setValidity('time', false);
43548 $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
43554 if (selected < min || selected > max) {
43555 ngModelCtrl.$setValidity('time', false);
43556 $scope.invalidHours = true;
43557 $scope.invalidMinutes = true;
43565 // Call internally when we know that model is valid.
43566 function refresh(keyboardChange) {
43568 ngModelCtrl.$setViewValue(new Date(selected));
43569 updateTemplate(keyboardChange);
43572 function makeValid() {
43573 ngModelCtrl.$setValidity('time', true);
43574 $scope.invalidHours = false;
43575 $scope.invalidMinutes = false;
43576 $scope.invalidSeconds = false;
43579 function updateTemplate(keyboardChange) {
43580 if (!ngModelCtrl.$modelValue) {
43581 $scope.hours = null;
43582 $scope.minutes = null;
43583 $scope.seconds = null;
43584 $scope.meridian = meridians[0];
43586 var hours = selected.getHours(),
43587 minutes = selected.getMinutes(),
43588 seconds = selected.getSeconds();
43590 if ($scope.showMeridian) {
43591 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
43595 $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours);
43597 $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
43598 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43599 if (keyboardChange !== 'm') {
43600 $scope.minutes = pad(minutes);
43602 $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
43604 if (keyboardChange !== 's') {
43605 $scope.seconds = pad(seconds);
43607 $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
43611 function addSecondsToSelected(seconds) {
43612 selected = addSeconds(selected, seconds);
43616 function addMinutes(selected, minutes) {
43617 return addSeconds(selected, minutes*60);
43620 function addSeconds(date, seconds) {
43621 var dt = new Date(date.getTime() + seconds * 1000);
43622 var newDate = new Date(date);
43623 newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
43628 function modelIsEmpty() {
43629 return ($scope.hours === null || $scope.hours === '') &&
43630 ($scope.minutes === null || $scope.minutes === '') &&
43631 (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === ''));
43635 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43636 $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
43637 $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
43639 $scope.incrementHours = function() {
43640 if (!$scope.noIncrementHours()) {
43641 addSecondsToSelected(hourStep * 60 * 60);
43645 $scope.decrementHours = function() {
43646 if (!$scope.noDecrementHours()) {
43647 addSecondsToSelected(-hourStep * 60 * 60);
43651 $scope.incrementMinutes = function() {
43652 if (!$scope.noIncrementMinutes()) {
43653 addSecondsToSelected(minuteStep * 60);
43657 $scope.decrementMinutes = function() {
43658 if (!$scope.noDecrementMinutes()) {
43659 addSecondsToSelected(-minuteStep * 60);
43663 $scope.incrementSeconds = function() {
43664 if (!$scope.noIncrementSeconds()) {
43665 addSecondsToSelected(secondStep);
43669 $scope.decrementSeconds = function() {
43670 if (!$scope.noDecrementSeconds()) {
43671 addSecondsToSelected(-secondStep);
43675 $scope.toggleMeridian = function() {
43676 var minutes = getMinutesFromTemplate(),
43677 hours = getHoursFromTemplate();
43679 if (!$scope.noToggleMeridian()) {
43680 if (angular.isDefined(minutes) && angular.isDefined(hours)) {
43681 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
43683 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
43688 $scope.blur = function() {
43689 ngModelCtrl.$setTouched();
43693 $scope.$on('$destroy', function() {
43694 while (watchers.length) {
43695 watchers.shift()();
43700 .directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {
43704 .directive('uibTimepicker', function() {
43705 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43707 require: ['uibTimepicker', '?^ngModel'],
43708 controller: 'UibTimepickerController',
43709 controllerAs: 'timepicker',
43712 templateUrl: function(element, attrs) {
43714 return attrs.templateUrl || uibTimepickerConfig.templateUrl;
43716 return attrs.templateUrl || 'uib/template/timepicker/timepicker.html';
43717 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43719 link: function(scope, element, attrs, ctrls) {
43720 var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
43723 timepickerCtrl.init(ngModelCtrl, element.find('input'));
43731 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43733 angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
43736 * A helper service that can parse typeahead's syntax (string provided by users)
43737 * Extracted to a separate service for ease of unit testing
43739 .factory('uibTypeaheadParser', ['$parse', function($parse) {
43740 // 00000111000000000000022200000000000000003333333333333330000000000044000
43741 var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
43743 parse: function(input) {
43744 var match = input.match(TYPEAHEAD_REGEXP);
43747 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
43748 ' but got "' + input + '".');
43752 itemName: match[3],
43753 source: $parse(match[4]),
43754 viewMapper: $parse(match[2] || match[1]),
43755 modelMapper: $parse(match[1])
43761 .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
43762 function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
43763 var HOT_KEYS = [9, 13, 27, 38, 40];
43764 var eventDebounceTime = 200;
43765 var modelCtrl, ngModelOptions;
43766 //SUPPORTED ATTRIBUTES (OPTIONS)
43768 //minimal no of characters that needs to be entered before typeahead kicks-in
43769 var minLength = originalScope.$eval(attrs.typeaheadMinLength);
43770 if (!minLength && minLength !== 0) {
43775 originalScope.$watch(attrs.typeaheadMinLength, function (newVal) {
43776 minLength = !newVal && newVal !== 0 ? 1 : newVal;
43780 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43781 //minimal wait time after last character typed before typeahead kicks-in
43782 var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
43784 //should it restrict model values to the ones selected from the popup only?
43785 var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
43786 originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
43787 isEditable = newVal !== false;
43790 //binding to a variable that indicates if matches are being retrieved asynchronously
43791 var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
43794 //a function to determine if an event should cause selection
43795 var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) {
43796 var evt = vals.$event;
43797 return evt.which === 13 || evt.which === 9;
43801 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43802 //a callback executed when a match is selected
43803 var onSelectCallback = $parse(attrs.typeaheadOnSelect);
43805 //should it select highlighted popup value when losing focus?
43806 var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
43808 //binding to a variable that indicates if there were no results after the query is completed
43809 var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
43811 var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
43813 var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
43815 var appendTo = attrs.typeaheadAppendTo ?
43816 originalScope.$eval(attrs.typeaheadAppendTo) : null;
43818 var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
43820 //If input matches an item of the list exactly, select it automatically
43821 var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
43823 //binding to a variable that indicates if dropdown is open
43824 var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
43826 var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
43828 //INTERNAL VARIABLES
43830 //model setter executed upon match selection
43831 var parsedModel = $parse(attrs.ngModel);
43832 var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
43833 var $setModelValue = function(scope, newValue) {
43834 if (angular.isFunction(parsedModel(originalScope)) &&
43835 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
43836 return invokeModelSetter(scope, {$$$p: newValue});
43839 return parsedModel.assign(scope, newValue);
43842 //expressions used by typeahead
43843 var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
43847 //Used to avoid bug in iOS webview where iOS keyboard does not fire
43848 //mousedown & mouseup events
43852 //create a child scope for the typeahead directive so we are not polluting original scope
43853 //with typeahead-specific data (matches, query etc.)
43854 var scope = originalScope.$new();
43855 var offDestroy = originalScope.$on('$destroy', function() {
43858 scope.$on('$destroy', offDestroy);
43861 var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
43863 'aria-autocomplete': 'list',
43864 'aria-expanded': false,
43865 'aria-owns': popupId
43868 var inputsContainer, hintInputElem;
43869 //add read-only input to show hint
43871 inputsContainer = angular.element('<div></div>');
43872 inputsContainer.css('position', 'relative');
43873 element.after(inputsContainer);
43874 hintInputElem = element.clone();
43875 hintInputElem.attr('placeholder', '');
43877 hintInputElem.attr('tabindex', '-1');
43879 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
43880 hintInputElem.val('');
43881 hintInputElem.css({
43882 'position': 'absolute',
43885 'border-color': 'transparent',
43886 'box-shadow': 'none',
43888 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
43892 'position': 'relative',
43893 'vertical-align': 'top',
43894 'background-color': 'transparent'
43896 inputsContainer.append(hintInputElem);
43897 hintInputElem.after(element);
43900 //pop-up element used to display matches
43901 var popUpEl = angular.element('<div uib-typeahead-popup></div>');
43904 matches: 'matches',
43905 active: 'activeIdx',
43906 select: 'select(activeIdx, evt)',
43907 'move-in-progress': 'moveInProgress',
43909 position: 'position',
43910 'assign-is-open': 'assignIsOpen(isOpen)',
43911 debounce: 'debounceUpdate'
43913 //custom item template
43914 if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
43915 popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
43918 if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
43919 popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
43922 var resetHint = function() {
43924 hintInputElem.val('');
43928 var resetMatches = function() {
43929 scope.matches = [];
43930 scope.activeIdx = -1;
43931 element.attr('aria-expanded', false);
43935 var getMatchId = function(index) {
43936 return popupId + '-option-' + index;
43939 // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
43940 // This attribute is added or removed automatically when the `activeIdx` changes.
43941 scope.$watch('activeIdx', function(index) {
43943 element.removeAttr('aria-activedescendant');
43945 element.attr('aria-activedescendant', getMatchId(index));
43949 var inputIsExactMatch = function(inputValue, index) {
43950 if (scope.matches.length > index && inputValue) {
43951 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
43957 var getMatchesAsync = function(inputValue, evt) {
43958 var locals = {$viewValue: inputValue};
43959 isLoadingSetter(originalScope, true);
43960 isNoResultsSetter(originalScope, false);
43961 $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
43962 //it might happen that several async queries were in progress if a user were typing fast
43963 //but we are interested only in responses that correspond to the current view value
43964 var onCurrentRequest = inputValue === modelCtrl.$viewValue;
43965 if (onCurrentRequest && hasFocus) {
43966 if (matches && matches.length > 0) {
43967 scope.activeIdx = focusFirst ? 0 : -1;
43968 isNoResultsSetter(originalScope, false);
43969 scope.matches.length = 0;
43972 for (var i = 0; i < matches.length; i++) {
43973 locals[parserResult.itemName] = matches[i];
43974 scope.matches.push({
43976 label: parserResult.viewMapper(scope, locals),
43981 scope.query = inputValue;
43982 //position pop-up with matches - we need to re-calculate its position each time we are opening a window
43983 //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
43984 //due to other elements being rendered
43985 recalculatePosition();
43987 element.attr('aria-expanded', true);
43989 //Select the single remaining option if user input matches
43990 if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
43991 if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
43992 $$debounce(function() {
43993 scope.select(0, evt);
43994 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
43996 scope.select(0, evt);
44001 var firstLabel = scope.matches[0].label;
44003 if (angular.isString(inputValue) &&
44004 inputValue.length > 0 &&
44005 firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
44006 hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
44009 if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
44010 hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
44013 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
44014 hintInputElem.val('');
44019 isNoResultsSetter(originalScope, true);
44022 if (onCurrentRequest) {
44023 isLoadingSetter(originalScope, false);
44027 isLoadingSetter(originalScope, false);
44028 isNoResultsSetter(originalScope, true);
44032 // bind events only if appendToBody params exist - performance feature
44033 if (appendToBody) {
44034 angular.element($window).on('resize', fireRecalculating);
44035 $document.find('body').on('scroll', fireRecalculating);
44038 // Declare the debounced function outside recalculating for
44039 // proper debouncing
44040 var debouncedRecalculate = $$debounce(function() {
44041 // if popup is visible
44042 if (scope.matches.length) {
44043 recalculatePosition();
44046 scope.moveInProgress = false;
44047 }, eventDebounceTime);
44049 // Default progress type
44050 scope.moveInProgress = false;
44052 function fireRecalculating() {
44053 if (!scope.moveInProgress) {
44054 scope.moveInProgress = true;
44058 debouncedRecalculate();
44061 // recalculate actual position and set new values to scope
44062 // after digest loop is popup in right position
44063 function recalculatePosition() {
44064 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
44065 scope.position.top += element.prop('offsetHeight');
44068 //we need to propagate user's query so we can higlight matches
44069 scope.query = undefined;
44071 //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
44072 var timeoutPromise;
44074 var scheduleSearchWithTimeout = function(inputValue) {
44075 timeoutPromise = $timeout(function() {
44076 getMatchesAsync(inputValue);
44080 var cancelPreviousTimeout = function() {
44081 if (timeoutPromise) {
44082 $timeout.cancel(timeoutPromise);
44088 scope.assignIsOpen = function (isOpen) {
44089 isOpenSetter(originalScope, isOpen);
44092 scope.select = function(activeIdx, evt) {
44093 //called from within the $digest() cycle
44098 locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
44099 model = parserResult.modelMapper(originalScope, locals);
44100 $setModelValue(originalScope, model);
44101 modelCtrl.$setValidity('editable', true);
44102 modelCtrl.$setValidity('parse', true);
44104 onSelectCallback(originalScope, {
44107 $label: parserResult.viewMapper(originalScope, locals),
44113 //return focus to the input element if a match was selected via a mouse click event
44114 // use timeout to avoid $rootScope:inprog error
44115 if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
44116 $timeout(function() { element[0].focus(); }, 0, false);
44120 //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
44121 element.on('keydown', function(evt) {
44122 //typeahead is open and an "interesting" key was pressed
44123 if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
44128 var shouldSelect = isSelectEvent(originalScope, {$event: evt});
44131 * if there's nothing selected (i.e. focusFirst) and enter or tab is hit
44133 * shift + tab is pressed to bring focus to the previous element
44134 * then clear the results
44136 if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) {
44138 // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
44139 if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
44140 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
44146 evt.preventDefault();
44149 switch (evt.which) {
44151 evt.stopPropagation();
44154 originalScope.$digest();
44156 case 38: // up arrow
44157 scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
44159 target = popUpEl.find('li')[scope.activeIdx];
44160 target.parentNode.scrollTop = target.offsetTop;
44162 case 40: // down arrow
44163 scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
44165 target = popUpEl.find('li')[scope.activeIdx];
44166 target.parentNode.scrollTop = target.offsetTop;
44169 if (shouldSelect) {
44170 scope.$apply(function() {
44171 if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
44172 $$debounce(function() {
44173 scope.select(scope.activeIdx, evt);
44174 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
44176 scope.select(scope.activeIdx, evt);
44182 switch (evt.which) {
44185 scope.$apply(function () {
44186 if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
44187 $$debounce(function() {
44188 scope.select(scope.activeIdx, evt);
44189 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
44191 scope.select(scope.activeIdx, evt);
44196 evt.stopPropagation();
44202 scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
44204 popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
44207 scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
44209 popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
44211 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
44215 element.bind('focus', function (evt) {
44217 if (minLength === 0 && !modelCtrl.$viewValue) {
44218 $timeout(function() {
44219 getMatchesAsync(modelCtrl.$viewValue, evt);
44224 element.bind('blur', function(evt) {
44225 if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
44227 scope.$apply(function() {
44228 if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
44229 $$debounce(function() {
44230 scope.select(scope.activeIdx, evt);
44231 }, scope.debounceUpdate.blur);
44233 scope.select(scope.activeIdx, evt);
44237 if (!isEditable && modelCtrl.$error.editable) {
44239 modelCtrl.$setViewValue();
44240 // Reset validity as we are clearing
44241 modelCtrl.$setValidity('editable', true);
44242 modelCtrl.$setValidity('parse', true);
44244 modelCtrl.$viewValue = '';
44245 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
44252 // Keep reference to click handler to unbind it.
44253 var dismissClickHandler = function(evt) {
44255 // Firefox treats right click as a click on document
44256 if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
44258 if (!$rootScope.$$phase) {
44260 originalScope.$digest();
44263 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
44268 $document.on('click', dismissClickHandler);
44270 originalScope.$on('$destroy', function() {
44271 $document.off('click', dismissClickHandler);
44272 if (appendToBody || appendTo) {
44276 if (appendToBody) {
44277 angular.element($window).off('resize', fireRecalculating);
44278 $document.find('body').off('scroll', fireRecalculating);
44280 // Prevent jQuery cache memory leak
44284 inputsContainer.remove();
44288 var $popup = $compile(popUpEl)(scope);
44290 if (appendToBody) {
44291 $document.find('body').append($popup);
44292 } else if (appendTo) {
44293 angular.element(appendTo).eq(0).append($popup);
44295 element.after($popup);
44298 this.init = function(_modelCtrl, _ngModelOptions) {
44299 modelCtrl = _modelCtrl;
44300 ngModelOptions = _ngModelOptions;
44302 scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
44304 //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
44305 //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
44306 modelCtrl.$parsers.unshift(function(inputValue) {
44309 if (minLength === 0 || inputValue && inputValue.length >= minLength) {
44310 if (waitTime > 0) {
44311 cancelPreviousTimeout();
44312 scheduleSearchWithTimeout(inputValue);
44314 getMatchesAsync(inputValue);
44317 isLoadingSetter(originalScope, false);
44318 cancelPreviousTimeout();
44327 // Reset in case user had typed something previously.
44328 modelCtrl.$setValidity('editable', true);
44332 modelCtrl.$setValidity('editable', false);
44336 modelCtrl.$formatters.push(function(modelValue) {
44337 var candidateViewValue, emptyViewValue;
44340 // The validity may be set to false via $parsers (see above) if
44341 // the model is restricted to selected values. If the model
44342 // is set manually it is considered to be valid.
44344 modelCtrl.$setValidity('editable', true);
44347 if (inputFormatter) {
44348 locals.$model = modelValue;
44349 return inputFormatter(originalScope, locals);
44352 //it might happen that we don't have enough info to properly render input value
44353 //we need to check for this situation and simply return model value if we can't apply custom formatting
44354 locals[parserResult.itemName] = modelValue;
44355 candidateViewValue = parserResult.viewMapper(originalScope, locals);
44356 locals[parserResult.itemName] = undefined;
44357 emptyViewValue = parserResult.viewMapper(originalScope, locals);
44359 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
44364 .directive('uibTypeahead', function() {
44366 controller: 'UibTypeaheadController',
44367 require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
44368 link: function(originalScope, element, attrs, ctrls) {
44369 ctrls[2].init(ctrls[0], ctrls[1]);
44374 .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
44381 moveInProgress: '=',
44387 templateUrl: function(element, attrs) {
44389 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
44391 link: function(scope, element, attrs) {
44392 scope.templateUrl = attrs.templateUrl;
44394 scope.isOpen = function() {
44395 var isDropdownOpen = scope.matches.length > 0;
44396 scope.assignIsOpen({ isOpen: isDropdownOpen });
44397 return isDropdownOpen;
44400 scope.isActive = function(matchIdx) {
44401 return scope.active === matchIdx;
44404 scope.selectActive = function(matchIdx) {
44405 scope.active = matchIdx;
44408 scope.selectMatch = function(activeIdx, evt) {
44409 var debounce = scope.debounce();
44410 if (angular.isNumber(debounce) || angular.isObject(debounce)) {
44411 $$debounce(function() {
44412 scope.select({activeIdx: activeIdx, evt: evt});
44413 }, angular.isNumber(debounce) ? debounce : debounce['default']);
44415 scope.select({activeIdx: activeIdx, evt: evt});
44422 .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
44429 link: function(scope, element, attrs) {
44430 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
44431 $templateRequest(tplUrl).then(function(tplContent) {
44432 var tplEl = angular.element(tplContent.trim());
44433 element.replaceWith(tplEl);
44434 $compile(tplEl)(scope);
44440 .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
44441 var isSanitizePresent;
44442 isSanitizePresent = $injector.has('$sanitize');
44444 function escapeRegexp(queryToEscape) {
44445 // Regex: capture the whole query string and replace it with the string that will be used to match
44446 // the results, for example if the capture is "a" the result will be \a
44447 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
44450 function containsHtml(matchItem) {
44451 return /<.*>/g.test(matchItem);
44454 return function(matchItem, query) {
44455 if (!isSanitizePresent && containsHtml(matchItem)) {
44456 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
44458 matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
44459 if (!isSanitizePresent) {
44460 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
44466 angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
44467 $templateCache.put("uib/template/accordion/accordion-group.html",
44468 "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
44469 " <div role=\"tab\" id=\"{{::headingId}}\" aria-selected=\"{{isOpen}}\" class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
44470 " <h4 class=\"panel-title\">\n" +
44471 " <a role=\"button\" data-toggle=\"collapse\" href aria-expanded=\"{{isOpen}}\" aria-controls=\"{{::panelId}}\" tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span uib-accordion-header ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
44474 " <div id=\"{{::panelId}}\" aria-labelledby=\"{{::headingId}}\" aria-hidden=\"{{!isOpen}}\" role=\"tabpanel\" class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
44475 " <div class=\"panel-body\" ng-transclude></div>\n" +
44481 angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
44482 $templateCache.put("uib/template/accordion/accordion.html",
44483 "<div role=\"tablist\" class=\"panel-group\" ng-transclude></div>");
44486 angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
44487 $templateCache.put("uib/template/alert/alert.html",
44488 "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
44489 " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
44490 " <span aria-hidden=\"true\">×</span>\n" +
44491 " <span class=\"sr-only\">Close</span>\n" +
44493 " <div ng-transclude></div>\n" +
44498 angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
44499 $templateCache.put("uib/template/carousel/carousel.html",
44500 "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
44501 " <div class=\"carousel-inner\" ng-transclude></div>\n" +
44502 " <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-class=\"{ disabled: isPrevDisabled() }\" ng-show=\"slides.length > 1\">\n" +
44503 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
44504 " <span class=\"sr-only\">previous</span>\n" +
44506 " <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-class=\"{ disabled: isNextDisabled() }\" ng-show=\"slides.length > 1\">\n" +
44507 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
44508 " <span class=\"sr-only\">next</span>\n" +
44510 " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
44511 " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
44512 " <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
44519 angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
44520 $templateCache.put("uib/template/carousel/slide.html",
44521 "<div ng-class=\"{\n" +
44522 " 'active': active\n" +
44523 " }\" class=\"item text-center\" ng-transclude></div>\n" +
44527 angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
44528 $templateCache.put("uib/template/datepicker/datepicker.html",
44529 "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
44530 " <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
44531 " <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
44532 " <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
44537 angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
44538 $templateCache.put("uib/template/datepicker/day.html",
44539 "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
44542 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
44543 " <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
44544 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
44547 " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
44548 " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
44552 " <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
44553 " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
44554 " <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
44555 " id=\"{{::dt.uid}}\"\n" +
44556 " ng-class=\"::dt.customClass\">\n" +
44557 " <button type=\"button\" class=\"btn btn-default btn-sm\"\n" +
44558 " uib-is-class=\"\n" +
44559 " 'btn-info' for selectedDt,\n" +
44560 " 'active' for activeDt\n" +
44562 " ng-click=\"select(dt.date)\"\n" +
44563 " ng-disabled=\"::dt.disabled\"\n" +
44564 " tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
44572 angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
44573 $templateCache.put("uib/template/datepicker/month.html",
44574 "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
44577 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
44578 " <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
44579 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
44583 " <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
44584 " <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
44585 " id=\"{{::dt.uid}}\"\n" +
44586 " ng-class=\"::dt.customClass\">\n" +
44587 " <button type=\"button\" class=\"btn btn-default\"\n" +
44588 " uib-is-class=\"\n" +
44589 " 'btn-info' for selectedDt,\n" +
44590 " 'active' for activeDt\n" +
44592 " ng-click=\"select(dt.date)\"\n" +
44593 " ng-disabled=\"::dt.disabled\"\n" +
44594 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
44602 angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
44603 $templateCache.put("uib/template/datepicker/year.html",
44604 "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
44607 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
44608 " <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
44609 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
44613 " <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
44614 " <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
44615 " id=\"{{::dt.uid}}\"\n" +
44616 " ng-class=\"::dt.customClass\">\n" +
44617 " <button type=\"button\" class=\"btn btn-default\"\n" +
44618 " uib-is-class=\"\n" +
44619 " 'btn-info' for selectedDt,\n" +
44620 " 'active' for activeDt\n" +
44622 " ng-click=\"select(dt.date)\"\n" +
44623 " ng-disabled=\"::dt.disabled\"\n" +
44624 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
44632 angular.module("uib/template/datepickerPopup/popup.html", []).run(["$templateCache", function($templateCache) {
44633 $templateCache.put("uib/template/datepickerPopup/popup.html",
44635 " <ul class=\"uib-datepicker-popup dropdown-menu uib-position-measure\" dropdown-nested ng-if=\"isOpen\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
44636 " <li ng-transclude></li>\n" +
44637 " <li ng-if=\"showButtonBar\" class=\"uib-button-bar\">\n" +
44638 " <span class=\"btn-group pull-left\">\n" +
44639 " <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today', $event)\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
44640 " <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null, $event)\">{{ getText('clear') }}</button>\n" +
44642 " <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close($event)\">{{ getText('close') }}</button>\n" +
44649 angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
44650 $templateCache.put("uib/template/modal/backdrop.html",
44651 "<div class=\"modal-backdrop\"\n" +
44652 " uib-modal-animation-class=\"fade\"\n" +
44653 " modal-in-class=\"in\"\n" +
44654 " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
44659 angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
44660 $templateCache.put("uib/template/modal/window.html",
44661 "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
44662 " uib-modal-animation-class=\"fade\"\n" +
44663 " modal-in-class=\"in\"\n" +
44664 " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
44665 " <div class=\"modal-dialog {{size ? 'modal-' + size : ''}}\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
44670 angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
44671 $templateCache.put("uib/template/pager/pager.html",
44672 "<ul class=\"pager\">\n" +
44673 " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
44674 " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
44679 angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
44680 $templateCache.put("uib/template/pagination/pagination.html",
44681 "<ul class=\"pagination\">\n" +
44682 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
44683 " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
44684 " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
44685 " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
44686 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
44691 angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
44692 $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
44693 "<div class=\"tooltip\"\n" +
44694 " tooltip-animation-class=\"fade\"\n" +
44695 " uib-tooltip-classes\n" +
44696 " ng-class=\"{ in: isOpen() }\">\n" +
44697 " <div class=\"tooltip-arrow\"></div>\n" +
44698 " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
44703 angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
44704 $templateCache.put("uib/template/tooltip/tooltip-popup.html",
44705 "<div class=\"tooltip\"\n" +
44706 " tooltip-animation-class=\"fade\"\n" +
44707 " uib-tooltip-classes\n" +
44708 " ng-class=\"{ in: isOpen() }\">\n" +
44709 " <div class=\"tooltip-arrow\"></div>\n" +
44710 " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
44715 angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
44716 $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
44717 "<div class=\"tooltip\"\n" +
44718 " tooltip-animation-class=\"fade\"\n" +
44719 " uib-tooltip-classes\n" +
44720 " ng-class=\"{ in: isOpen() }\">\n" +
44721 " <div class=\"tooltip-arrow\"></div>\n" +
44722 " <div class=\"tooltip-inner\"\n" +
44723 " uib-tooltip-template-transclude=\"contentExp()\"\n" +
44724 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
44729 angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
44730 $templateCache.put("uib/template/popover/popover-html.html",
44731 "<div class=\"popover\"\n" +
44732 " tooltip-animation-class=\"fade\"\n" +
44733 " uib-tooltip-classes\n" +
44734 " ng-class=\"{ in: isOpen() }\">\n" +
44735 " <div class=\"arrow\"></div>\n" +
44737 " <div class=\"popover-inner\">\n" +
44738 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
44739 " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
44745 angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
44746 $templateCache.put("uib/template/popover/popover-template.html",
44747 "<div class=\"popover\"\n" +
44748 " tooltip-animation-class=\"fade\"\n" +
44749 " uib-tooltip-classes\n" +
44750 " ng-class=\"{ in: isOpen() }\">\n" +
44751 " <div class=\"arrow\"></div>\n" +
44753 " <div class=\"popover-inner\">\n" +
44754 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
44755 " <div class=\"popover-content\"\n" +
44756 " uib-tooltip-template-transclude=\"contentExp()\"\n" +
44757 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
44763 angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
44764 $templateCache.put("uib/template/popover/popover.html",
44765 "<div class=\"popover\"\n" +
44766 " tooltip-animation-class=\"fade\"\n" +
44767 " uib-tooltip-classes\n" +
44768 " ng-class=\"{ in: isOpen() }\">\n" +
44769 " <div class=\"arrow\"></div>\n" +
44771 " <div class=\"popover-inner\">\n" +
44772 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
44773 " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
44779 angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
44780 $templateCache.put("uib/template/progressbar/bar.html",
44781 "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
44785 angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
44786 $templateCache.put("uib/template/progressbar/progress.html",
44787 "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
44790 angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
44791 $templateCache.put("uib/template/progressbar/progressbar.html",
44792 "<div class=\"progress\">\n" +
44793 " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
44798 angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
44799 $templateCache.put("uib/template/rating/rating.html",
44800 "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\" aria-valuetext=\"{{title}}\">\n" +
44801 " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
44802 " <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\"></i>\n" +
44807 angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
44808 $templateCache.put("uib/template/tabs/tab.html",
44809 "<li ng-class=\"[{active: active, disabled: disabled}, classes]\" class=\"uib-tab nav-item\">\n" +
44810 " <a href ng-click=\"select($event)\" class=\"nav-link\" uib-tab-heading-transclude>{{heading}}</a>\n" +
44815 angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
44816 $templateCache.put("uib/template/tabs/tabset.html",
44818 " <ul class=\"nav nav-{{tabset.type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
44819 " <div class=\"tab-content\">\n" +
44820 " <div class=\"tab-pane\"\n" +
44821 " ng-repeat=\"tab in tabset.tabs\"\n" +
44822 " ng-class=\"{active: tabset.active === tab.index}\"\n" +
44823 " uib-tab-content-transclude=\"tab\">\n" +
44830 angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
44831 $templateCache.put("uib/template/timepicker/timepicker.html",
44832 "<table class=\"uib-timepicker\">\n" +
44834 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
44835 " <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
44836 " <td> </td>\n" +
44837 " <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
44838 " <td ng-show=\"showSeconds\"> </td>\n" +
44839 " <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
44840 " <td ng-show=\"showMeridian\"></td>\n" +
44843 " <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
44844 " <input type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementHours()\" ng-blur=\"blur()\">\n" +
44846 " <td class=\"uib-separator\">:</td>\n" +
44847 " <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
44848 " <input type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementMinutes()\" ng-blur=\"blur()\">\n" +
44850 " <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
44851 " <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
44852 " <input type=\"text\" placeholder=\"SS\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementSeconds()\" ng-blur=\"blur()\">\n" +
44854 " <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
44856 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
44857 " <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
44858 " <td> </td>\n" +
44859 " <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
44860 " <td ng-show=\"showSeconds\"> </td>\n" +
44861 " <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
44862 " <td ng-show=\"showMeridian\"></td>\n" +
44869 angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
44870 $templateCache.put("uib/template/typeahead/typeahead-match.html",
44872 " tabindex=\"-1\"\n" +
44873 " ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"\n" +
44874 " ng-attr-title=\"{{match.label}}\"></a>\n" +
44878 angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
44879 $templateCache.put("uib/template/typeahead/typeahead-popup.html",
44880 "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
44881 " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
44882 " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
44887 angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); angular.$$uibCarouselCss = true; });
44888 angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker .uib-title{width:100%;}.uib-day button,.uib-month button,.uib-year button{min-width:100%;}.uib-left,.uib-right{width:100%}</style>'); angular.$$uibDatepickerCss = true; });
44889 angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'); angular.$$uibPositionCss = true; });
44890 angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0;}.uib-button-bar{padding:10px 9px 2px;}</style>'); angular.$$uibDatepickerpopupCss = true; });
44891 angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'); angular.$$uibTooltipCss = true; });
44892 angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-time input{width:50px;}</style>'); angular.$$uibTimepickerCss = true; });
44893 angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'); angular.$$uibTypeaheadCss = true; });
44897 /***/ function(module, exports) {
44902 (function (declares) {
44903 var CommandInfo = (function () {
44904 function CommandInfo(name) {
44907 return CommandInfo;
44909 declares.CommandInfo = CommandInfo;
44910 })(declares = app.declares || (app.declares = {}));
44911 })(app || (app = {}));
44915 (function (services) {
44916 var APIEndPoint = (function () {
44917 function APIEndPoint($resource, $http) {
44918 this.$resource = $resource;
44919 this.$http = $http;
44921 APIEndPoint.prototype.resource = function (endPoint, data) {
44922 var customAction = {
44928 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
44930 return this.$resource(endPoint, {}, { execute: execute });
44932 APIEndPoint.prototype.getOptionControlFile = function (command) {
44933 var endPoint = '/api/v1/optionControlFile/' + command;
44934 return this.resource(endPoint, {}).get();
44936 APIEndPoint.prototype.getFiles = function (fileId) {
44937 var endPoint = '/api/v1/workspace';
44939 endPoint += '/' + fileId;
44941 return this.resource(endPoint, {}).get();
44943 APIEndPoint.prototype.getDirectories = function () {
44944 var endPoint = '/api/v1/all/workspace/directory';
44945 return this.resource(endPoint, {}).get();
44947 APIEndPoint.prototype.getTags = function () {
44948 var endPoint = '/api/v1/tagList';
44949 return this.resource(endPoint, {}).get();
44951 APIEndPoint.prototype.getCommands = function () {
44952 var endPoint = '/api/v1/commandList';
44953 return this.resource(endPoint, {}).get();
44955 APIEndPoint.prototype.execute = function (data) {
44956 var endPoint = '/api/v1/execution';
44957 var fd = new FormData();
44958 fd.append('data', data);
44959 return this.$http.post(endPoint, fd, {
44960 headers: { 'Content-Type': undefined },
44961 transformRequest: angular.identity
44964 APIEndPoint.prototype.debug = function () {
44965 var endPoint = '/api/v1/debug';
44966 return this.$http.get(endPoint);
44968 APIEndPoint.prototype.upload = function () {
44969 var endPoint = '/api/v1/upload';
44970 return this.$http.get(endPoint);
44972 APIEndPoint.prototype.help = function (command) {
44973 var endPoint = '/api/v1/help/' + command;
44974 return this.$http.get(endPoint);
44976 return APIEndPoint;
44978 services.APIEndPoint = APIEndPoint;
44979 })(services = app.services || (app.services = {}));
44980 })(app || (app = {}));
44984 (function (services) {
44985 var MyModal = (function () {
44986 function MyModal($uibModal) {
44987 this.$uibModal = $uibModal;
44988 this.modalOption = {
44995 MyModal.prototype.open = function (modalName) {
44996 if (modalName === 'SelectCommand') {
44997 this.modalOption.templateUrl = 'templates/select-command.html';
44998 this.modalOption.size = 'lg';
45000 return this.$uibModal.open(this.modalOption);
45002 MyModal.prototype.selectCommand = function () {
45003 this.modalOption.templateUrl = 'templates/select-command.html';
45004 this.modalOption.controller = 'selectCommandController';
45005 this.modalOption.controllerAs = 'c';
45006 this.modalOption.size = 'lg';
45007 return this.$uibModal.open(this.modalOption);
45009 MyModal.prototype.preview = function () {
45010 this.modalOption.templateUrl = 'templates/preview.html';
45011 this.modalOption.controller = 'previewController';
45012 this.modalOption.controllerAs = 'c';
45013 this.modalOption.size = 'lg';
45014 return this.$uibModal.open(this.modalOption);
45016 MyModal.prototype.upload = function () {
45017 this.modalOption.templateUrl = 'templates/upload.html';
45018 this.modalOption.controller = 'uploadController';
45019 this.modalOption.controllerAs = 'c';
45020 this.modalOption.size = 'lg';
45021 return this.$uibModal.open(this.modalOption);
45023 MyModal.$inject = ['$uibModal'];
45026 services.MyModal = MyModal;
45027 })(services = app.services || (app.services = {}));
45028 })(app || (app = {}));
45032 (function (services) {
45033 var WebSocket = (function () {
45034 function WebSocket($rootScope) {
45035 this.$rootScope = $rootScope;
45036 this.socket = io.connect();
45038 WebSocket.prototype.on = function (eventName, callback) {
45039 var socket = this.socket;
45040 var rootScope = this.$rootScope;
45041 socket.on(eventName, function () {
45042 var args = arguments;
45043 rootScope.$apply(function () {
45044 callback.apply(socket, args);
45048 WebSocket.prototype.emit = function (eventName, data, callback) {
45049 var socket = this.socket;
45050 var rootScope = this.$rootScope;
45051 this.socket.emit(eventName, data, function () {
45052 var args = arguments;
45053 rootScope.$apply(function () {
45055 callback.apply(socket, args);
45061 services.WebSocket = WebSocket;
45062 })(services = app.services || (app.services = {}));
45063 })(app || (app = {}));
45067 (function (services) {
45068 var Console = (function () {
45069 function Console(WebSocket, $rootScope) {
45070 this.WebSocket = WebSocket;
45071 this.$rootScope = $rootScope;
45072 this.WebSocket = WebSocket;
45073 this.$rootScope = $rootScope;
45074 this.directiveIDs = [];
45075 var directiveIDs = this.directiveIDs;
45076 this.WebSocket.on('console', function (d) {
45078 var message = d.message;
45079 if (directiveIDs.indexOf(id) > -1) {
45080 $rootScope.$emit(id, message);
45084 Console.prototype.addDirective = function (id) {
45085 if (!(this.directiveIDs.indexOf(id) > -1)) {
45086 this.directiveIDs.push(id);
45089 Console.prototype.removeDirective = function (id) {
45090 var i = this.directiveIDs.indexOf(id);
45092 this.directiveIDs.splice(i, 1);
45095 Console.prototype.showIDs = function () {
45096 console.log(this.directiveIDs);
45100 services.Console = Console;
45101 })(services = app.services || (app.services = {}));
45102 })(app || (app = {}));
45106 (function (directives) {
45107 var Command = (function () {
45108 function Command() {
45109 this.restrict = 'E';
45110 this.replace = true;
45112 this.controller = 'commandController';
45113 this.controllerAs = 'ctrl';
45114 this.bindToController = {
45120 this.templateUrl = 'templates/command.html';
45122 Command.Factory = function () {
45123 var directive = function () {
45124 return new Command();
45126 directive.$inject = [];
45131 directives.Command = Command;
45132 var CommandController = (function () {
45133 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
45134 this.APIEndPoint = APIEndPoint;
45135 this.$scope = $scope;
45136 this.MyModal = MyModal;
45137 this.WebSocket = WebSocket;
45138 this.$window = $window;
45139 this.$rootScope = $rootScope;
45140 this.Console = Console;
45141 var controller = this;
45143 .getOptionControlFile(this.name)
45145 .then(function (result) {
45146 controller.options = result.info;
45151 .then(function (result) {
45152 controller.dirs = result.info;
45154 this.heading = "[" + this.index + "]: dcdFilePrint";
45155 this.isOpen = true;
45156 this.$scope.$on('close', function () {
45157 controller.isOpen = false;
45161 return Math.floor((1 + Math.random()) * 0x10000)
45165 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
45166 s4() + '-' + s4() + s4() + s4();
45168 this.uuid = guid();
45169 this.Console.addDirective(this.uuid);
45170 this.Console.showIDs();
45172 CommandController.prototype.submit = function () {
45174 angular.forEach(this.options, function (option) {
45176 name: option.option,
45179 angular.forEach(option.arg, function (arg) {
45181 if (typeof arg.input === 'object') {
45182 obj.arguments.push(arg.input.name);
45185 obj.arguments.push(arg.input);
45189 if (obj.arguments.length > 0) {
45194 command: this.name,
45195 workspace: this.workspace.fileId,
45199 .execute(JSON.stringify(execObj))
45200 .then(function (result) {
45201 console.log(result);
45204 CommandController.prototype.removeMySelf = function (index) {
45205 this.$scope.$destroy();
45206 this.Console.removeDirective(this.uuid);
45207 this.remove()(index, this.list);
45208 this.Console.showIDs();
45210 CommandController.prototype.reloadFiles = function () {
45212 var fileId = this.workspace.fileId;
45216 .then(function (result) {
45217 var status = result.status;
45218 if (status === 'success') {
45219 _this.files = result.info;
45222 console.log(result.message);
45226 CommandController.prototype.debug = function () {
45227 var div = angular.element(this.$window.document).find("div");
45230 angular.forEach(div, function (v) {
45231 if (v.className === "panel-body console") {
45234 else if (v.className === "row parameters-console") {
45238 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
45239 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
45240 consoleTag.style.height = consoleHeight;
45241 consoleTag.style.width = consoleWidth;
45243 CommandController.prototype.help = function () {
45246 .then(function (result) {
45247 console.log(result);
45250 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
45251 return CommandController;
45253 directives.CommandController = CommandController;
45254 })(directives = app.directives || (app.directives = {}));
45255 })(app || (app = {}));
45259 (function (directives) {
45260 var HeaderMenu = (function () {
45261 function HeaderMenu() {
45262 this.restrict = 'E';
45263 this.replace = true;
45264 this.templateUrl = 'templates/header-menu.html';
45265 this.controller = 'HeaderMenuController';
45266 this.controllerAs = 'hmc';
45269 HeaderMenu.Factory = function () {
45270 var directive = function () {
45271 return new HeaderMenu();
45277 directives.HeaderMenu = HeaderMenu;
45278 var HeaderMenuController = (function () {
45279 function HeaderMenuController($state) {
45280 this.$state = $state;
45281 this.isExecution = this.$state.current.name === 'execution';
45282 this.isWorkspace = this.$state.current.name === 'workspace';
45283 this.isHistory = this.$state.current.name === 'history';
45285 HeaderMenuController.prototype.transit = function (state) {
45286 this.$state.go(state);
45288 HeaderMenuController.$inject = ['$state'];
45289 return HeaderMenuController;
45291 directives.HeaderMenuController = HeaderMenuController;
45292 })(directives = app.directives || (app.directives = {}));
45293 })(app || (app = {}));
45297 (function (directives) {
45298 var Option = (function () {
45299 function Option() {
45300 this.restrict = 'E';
45301 this.replace = true;
45302 this.controller = 'optionController';
45303 this.bindToController = {
45308 this.templateUrl = 'templates/option.html';
45309 this.controllerAs = 'ctrl';
45311 Option.Factory = function () {
45312 var directive = function () {
45313 return new Option();
45315 directive.$inject = [];
45320 directives.Option = Option;
45321 var OptionController = (function () {
45322 function OptionController() {
45323 var controller = this;
45324 angular.forEach(controller.info.arg, function (arg) {
45325 if (arg.initialValue) {
45326 if (arg.formType === 'number') {
45327 arg.input = parseInt(arg.initialValue);
45330 arg.input = arg.initialValue;
45335 OptionController.$inject = [];
45336 return OptionController;
45338 directives.OptionController = OptionController;
45339 })(directives = app.directives || (app.directives = {}));
45340 })(app || (app = {}));
45344 (function (directives) {
45345 var Directory = (function () {
45346 function Directory() {
45347 this.restrict = 'E';
45348 this.replace = true;
45349 this.controller = 'directoryController';
45350 this.controllerAs = 'ctrl';
45351 this.bindToController = {
45357 this.templateUrl = 'templates/directory.html';
45359 Directory.Factory = function () {
45360 var directive = function () {
45361 return new Directory();
45367 directives.Directory = Directory;
45368 var DirectoryController = (function () {
45369 function DirectoryController(APIEndPoint, $scope) {
45370 this.APIEndPoint = APIEndPoint;
45371 this.$scope = $scope;
45372 var controller = this;
45374 .getFiles(this.info.fileId)
45376 .then(function (result) {
45377 if (result.status === 'success') {
45378 controller.files = result.info;
45379 angular.forEach(result.info, function (file) {
45380 if (file.fileType === '0') {
45382 if (controller.info.path === '/') {
45383 o.path = '/' + file.name;
45386 o.path = controller.info.path + '/' + file.name;
45388 controller.add()(o, controller.list);
45395 DirectoryController.$inject = ['APIEndPoint', '$scope'];
45396 return DirectoryController;
45398 directives.DirectoryController = DirectoryController;
45399 })(directives = app.directives || (app.directives = {}));
45400 })(app || (app = {}));
45404 (function (directives) {
45405 var Upload = (function () {
45406 function Upload() {
45407 this.restrict = 'E';
45408 this.replace = true;
45410 this.controller = 'UploadController';
45411 this.controllerAs = 'ctrl';
45412 this.bindToController = {
45418 this.templateUrl = 'templates/upload.html';
45419 console.log("templates/upload.html-constructor");
45421 Upload.Factory = function () {
45422 var directive = function () {
45423 return new Upload();
45425 directive.$inject = [];
45430 directives.Upload = Upload;
45431 var UploadController = (function () {
45432 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
45433 this.APIEndPoint = APIEndPoint;
45434 this.$scope = $scope;
45435 this.MyModal = MyModal;
45436 this.WebSocket = WebSocket;
45437 this.$window = $window;
45438 this.$rootScope = $rootScope;
45439 this.Console = Console;
45440 var controller = this;
45441 console.log("directive.upload-constructor");
45443 UploadController.prototype.submit = function () {
45444 console.log("submit: function not supported¥n");
45446 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
45447 return UploadController;
45449 directives.UploadController = UploadController;
45450 })(directives = app.directives || (app.directives = {}));
45451 })(app || (app = {}));
45455 (function (controllers) {
45456 var Execution = (function () {
45457 function Execution(MyModal, $scope) {
45458 this.MyModal = MyModal;
45459 this.$scope = $scope;
45460 this.commandInfoList = [];
45463 Execution.prototype.add = function () {
45464 this.$scope.$broadcast('close');
45465 var commandInfoList = this.commandInfoList;
45466 var commandInstance = this.MyModal.selectCommand();
45469 .then(function (command) {
45470 commandInfoList.push(new app.declares.CommandInfo(command));
45473 Execution.prototype.open = function () {
45474 var result = this.MyModal.open('SelectCommand');
45475 console.log(result);
45477 Execution.prototype.remove = function (index, list) {
45478 list.splice(index, 1);
45480 Execution.prototype.close = function () {
45481 console.log("close");
45483 Execution.$inject = ['MyModal', '$scope'];
45486 controllers.Execution = Execution;
45487 })(controllers = app.controllers || (app.controllers = {}));
45488 })(app || (app = {}));
45492 (function (controllers) {
45493 var Workspace = (function () {
45494 function Workspace($scope, APIEndPoint, MyModal) {
45495 this.$scope = $scope;
45496 this.APIEndPoint = APIEndPoint;
45497 this.MyModal = MyModal;
45498 this.directoryList = [];
45499 var controller = this;
45500 var directoryList = this.directoryList;
45502 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45510 directoryList.push(o);
45512 Workspace.prototype.addDirectory = function (info, directoryList) {
45513 directoryList.push(info);
45515 Workspace.prototype.upload = function () {
45516 this.MyModal.upload();
45518 Workspace.prototype.debug = function () {
45519 this.MyModal.preview();
45521 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
45524 controllers.Workspace = Workspace;
45525 })(controllers = app.controllers || (app.controllers = {}));
45526 })(app || (app = {}));
45530 (function (controllers) {
45531 var History = (function () {
45532 function History($scope) {
45533 this.page = "History";
45535 History.$inject = ['$scope'];
45538 controllers.History = History;
45539 })(controllers = app.controllers || (app.controllers = {}));
45540 })(app || (app = {}));
45544 (function (controllers) {
45545 var SelectCommand = (function () {
45546 function SelectCommand($scope, APIEndPoint, $modalInstance) {
45547 this.APIEndPoint = APIEndPoint;
45548 this.$modalInstance = $modalInstance;
45549 var controller = this;
45552 .$promise.then(function (result) {
45553 controller.tags = result.info;
45557 .$promise.then(function (result) {
45558 controller.commands = result.info;
45560 this.currentTag = 'all';
45562 SelectCommand.prototype.changeTag = function (tag) {
45563 this.currentTag = tag;
45565 SelectCommand.prototype.selectCommand = function (command) {
45566 this.$modalInstance.close(command);
45568 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45569 return SelectCommand;
45571 controllers.SelectCommand = SelectCommand;
45572 })(controllers = app.controllers || (app.controllers = {}));
45573 })(app || (app = {}));
45577 (function (controllers) {
45578 var Upload = (function () {
45579 function Upload($scope, APIEndPoint, $modalInstance) {
45580 this.APIEndPoint = APIEndPoint;
45581 this.$modalInstance = $modalInstance;
45582 var controller = this;
45583 console.log('controller.upload-controllers');
45585 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45588 controllers.Upload = Upload;
45589 })(controllers = app.controllers || (app.controllers = {}));
45590 })(app || (app = {}));
45594 (function (controllers) {
45595 var Preview = (function () {
45596 function Preview($scope, APIEndPoint, $modalInstance) {
45597 this.APIEndPoint = APIEndPoint;
45598 this.$modalInstance = $modalInstance;
45599 var controller = this;
45600 console.log('preview');
45602 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45605 controllers.Preview = Preview;
45606 })(controllers = app.controllers || (app.controllers = {}));
45607 })(app || (app = {}));
45609 (function (filters) {
45611 return function (commands, tag) {
45613 angular.forEach(commands, function (command) {
45615 angular.forEach(command.tags, function (value) {
45620 result.push(command);
45626 })(filters || (filters = {}));
45630 var appName = 'zephyr';
45631 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45632 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45633 $urlRouterProvider.otherwise('/execution');
45634 $locationProvider.html5Mode({
45639 .state('execution', {
45641 templateUrl: 'templates/execution.html',
45642 controller: 'executionController',
45645 .state('workspace', {
45647 templateUrl: 'templates/workspace.html',
45648 controller: 'workspaceController',
45651 .state('history', {
45653 templateUrl: 'templates/history.html',
45654 controller: 'historyController',
45658 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45659 app.zephyr.service('MyModal', app.services.MyModal);
45660 app.zephyr.service('WebSocket', app.services.WebSocket);
45661 app.zephyr.service('Console', app.services.Console);
45662 app.zephyr.filter('Tag', filters.Tag);
45663 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
45664 app.zephyr.controller('previewController', app.controllers.Preview);
45665 app.zephyr.controller('uploadController', app.controllers.Upload);
45666 app.zephyr.controller('executionController', app.controllers.Execution);
45667 app.zephyr.controller('workspaceController', app.controllers.Workspace);
45668 app.zephyr.controller('historyController', app.controllers.History);
45669 app.zephyr.controller('commandController', app.directives.CommandController);
45670 app.zephyr.controller('optionController', app.directives.OptionController);
45671 app.zephyr.controller('directoryController', app.directives.DirectoryController);
45672 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
45673 app.zephyr.controller('uploadController', app.directives.UploadController);
45674 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45675 app.zephyr.directive('command', app.directives.Command.Factory());
45676 app.zephyr.directive('option', app.directives.Option.Factory());
45677 app.zephyr.directive('directory', app.directives.Directory.Factory());
45678 })(app || (app = {}));
45683 /***/ function(module, exports) {
45688 (function (declares) {
45689 var CommandInfo = (function () {
45690 function CommandInfo(name) {
45693 return CommandInfo;
45695 declares.CommandInfo = CommandInfo;
45696 })(declares = app.declares || (app.declares = {}));
45697 })(app || (app = {}));
45701 (function (services) {
45702 var APIEndPoint = (function () {
45703 function APIEndPoint($resource, $http) {
45704 this.$resource = $resource;
45705 this.$http = $http;
45707 APIEndPoint.prototype.resource = function (endPoint, data) {
45708 var customAction = {
45714 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
45716 return this.$resource(endPoint, {}, { execute: execute });
45718 APIEndPoint.prototype.getOptionControlFile = function (command) {
45719 var endPoint = '/api/v1/optionControlFile/' + command;
45720 return this.resource(endPoint, {}).get();
45722 APIEndPoint.prototype.getFiles = function (fileId) {
45723 var endPoint = '/api/v1/workspace';
45725 endPoint += '/' + fileId;
45727 return this.resource(endPoint, {}).get();
45729 APIEndPoint.prototype.getDirectories = function () {
45730 var endPoint = '/api/v1/all/workspace/directory';
45731 return this.resource(endPoint, {}).get();
45733 APIEndPoint.prototype.getTags = function () {
45734 var endPoint = '/api/v1/tagList';
45735 return this.resource(endPoint, {}).get();
45737 APIEndPoint.prototype.getCommands = function () {
45738 var endPoint = '/api/v1/commandList';
45739 return this.resource(endPoint, {}).get();
45741 APIEndPoint.prototype.execute = function (data) {
45742 var endPoint = '/api/v1/execution';
45743 var fd = new FormData();
45744 fd.append('data', data);
45745 return this.$http.post(endPoint, fd, {
45746 headers: { 'Content-Type': undefined },
45747 transformRequest: angular.identity
45750 APIEndPoint.prototype.debug = function () {
45751 var endPoint = '/api/v1/debug';
45752 return this.$http.get(endPoint);
45754 APIEndPoint.prototype.upload = function () {
45755 var endPoint = '/api/v1/upload';
45756 return this.$http.get(endPoint);
45758 APIEndPoint.prototype.help = function (command) {
45759 var endPoint = '/api/v1/help/' + command;
45760 return this.$http.get(endPoint);
45762 return APIEndPoint;
45764 services.APIEndPoint = APIEndPoint;
45765 })(services = app.services || (app.services = {}));
45766 })(app || (app = {}));
45770 (function (services) {
45771 var MyModal = (function () {
45772 function MyModal($uibModal) {
45773 this.$uibModal = $uibModal;
45774 this.modalOption = {
45781 MyModal.prototype.open = function (modalName) {
45782 if (modalName === 'SelectCommand') {
45783 this.modalOption.templateUrl = 'templates/select-command.html';
45784 this.modalOption.size = 'lg';
45786 return this.$uibModal.open(this.modalOption);
45788 MyModal.prototype.selectCommand = function () {
45789 this.modalOption.templateUrl = 'templates/select-command.html';
45790 this.modalOption.controller = 'selectCommandController';
45791 this.modalOption.controllerAs = 'c';
45792 this.modalOption.size = 'lg';
45793 return this.$uibModal.open(this.modalOption);
45795 MyModal.prototype.preview = function () {
45796 this.modalOption.templateUrl = 'templates/preview.html';
45797 this.modalOption.controller = 'previewController';
45798 this.modalOption.controllerAs = 'c';
45799 this.modalOption.size = 'lg';
45800 return this.$uibModal.open(this.modalOption);
45802 MyModal.prototype.upload = function () {
45803 this.modalOption.templateUrl = 'templates/upload.html';
45804 this.modalOption.controller = 'uploadController';
45805 this.modalOption.controllerAs = 'c';
45806 this.modalOption.size = 'lg';
45807 return this.$uibModal.open(this.modalOption);
45809 MyModal.$inject = ['$uibModal'];
45812 services.MyModal = MyModal;
45813 })(services = app.services || (app.services = {}));
45814 })(app || (app = {}));
45818 (function (services) {
45819 var WebSocket = (function () {
45820 function WebSocket($rootScope) {
45821 this.$rootScope = $rootScope;
45822 this.socket = io.connect();
45824 WebSocket.prototype.on = function (eventName, callback) {
45825 var socket = this.socket;
45826 var rootScope = this.$rootScope;
45827 socket.on(eventName, function () {
45828 var args = arguments;
45829 rootScope.$apply(function () {
45830 callback.apply(socket, args);
45834 WebSocket.prototype.emit = function (eventName, data, callback) {
45835 var socket = this.socket;
45836 var rootScope = this.$rootScope;
45837 this.socket.emit(eventName, data, function () {
45838 var args = arguments;
45839 rootScope.$apply(function () {
45841 callback.apply(socket, args);
45847 services.WebSocket = WebSocket;
45848 })(services = app.services || (app.services = {}));
45849 })(app || (app = {}));
45853 (function (services) {
45854 var Console = (function () {
45855 function Console(WebSocket, $rootScope) {
45856 this.WebSocket = WebSocket;
45857 this.$rootScope = $rootScope;
45858 this.WebSocket = WebSocket;
45859 this.$rootScope = $rootScope;
45860 this.directiveIDs = [];
45861 var directiveIDs = this.directiveIDs;
45862 this.WebSocket.on('console', function (d) {
45864 var message = d.message;
45865 if (directiveIDs.indexOf(id) > -1) {
45866 $rootScope.$emit(id, message);
45870 Console.prototype.addDirective = function (id) {
45871 if (!(this.directiveIDs.indexOf(id) > -1)) {
45872 this.directiveIDs.push(id);
45875 Console.prototype.removeDirective = function (id) {
45876 var i = this.directiveIDs.indexOf(id);
45878 this.directiveIDs.splice(i, 1);
45881 Console.prototype.showIDs = function () {
45882 console.log(this.directiveIDs);
45886 services.Console = Console;
45887 })(services = app.services || (app.services = {}));
45888 })(app || (app = {}));
45892 (function (directives) {
45893 var Command = (function () {
45894 function Command() {
45895 this.restrict = 'E';
45896 this.replace = true;
45898 this.controller = 'commandController';
45899 this.controllerAs = 'ctrl';
45900 this.bindToController = {
45906 this.templateUrl = 'templates/command.html';
45908 Command.Factory = function () {
45909 var directive = function () {
45910 return new Command();
45912 directive.$inject = [];
45917 directives.Command = Command;
45918 var CommandController = (function () {
45919 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
45920 this.APIEndPoint = APIEndPoint;
45921 this.$scope = $scope;
45922 this.MyModal = MyModal;
45923 this.WebSocket = WebSocket;
45924 this.$window = $window;
45925 this.$rootScope = $rootScope;
45926 this.Console = Console;
45927 var controller = this;
45929 .getOptionControlFile(this.name)
45931 .then(function (result) {
45932 controller.options = result.info;
45937 .then(function (result) {
45938 controller.dirs = result.info;
45940 this.heading = "[" + this.index + "]: dcdFilePrint";
45941 this.isOpen = true;
45942 this.$scope.$on('close', function () {
45943 controller.isOpen = false;
45947 return Math.floor((1 + Math.random()) * 0x10000)
45951 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
45952 s4() + '-' + s4() + s4() + s4();
45954 this.uuid = guid();
45955 this.Console.addDirective(this.uuid);
45956 this.Console.showIDs();
45958 CommandController.prototype.submit = function () {
45960 angular.forEach(this.options, function (option) {
45962 name: option.option,
45965 angular.forEach(option.arg, function (arg) {
45967 if (typeof arg.input === 'object') {
45968 obj.arguments.push(arg.input.name);
45971 obj.arguments.push(arg.input);
45975 if (obj.arguments.length > 0) {
45980 command: this.name,
45981 workspace: this.workspace.fileId,
45985 .execute(JSON.stringify(execObj))
45986 .then(function (result) {
45987 console.log(result);
45990 CommandController.prototype.removeMySelf = function (index) {
45991 this.$scope.$destroy();
45992 this.Console.removeDirective(this.uuid);
45993 this.remove()(index, this.list);
45994 this.Console.showIDs();
45996 CommandController.prototype.reloadFiles = function () {
45998 var fileId = this.workspace.fileId;
46002 .then(function (result) {
46003 var status = result.status;
46004 if (status === 'success') {
46005 _this.files = result.info;
46008 console.log(result.message);
46012 CommandController.prototype.debug = function () {
46013 var div = angular.element(this.$window.document).find("div");
46016 angular.forEach(div, function (v) {
46017 if (v.className === "panel-body console") {
46020 else if (v.className === "row parameters-console") {
46024 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
46025 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
46026 consoleTag.style.height = consoleHeight;
46027 consoleTag.style.width = consoleWidth;
46029 CommandController.prototype.help = function () {
46032 .then(function (result) {
46033 console.log(result);
46036 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
46037 return CommandController;
46039 directives.CommandController = CommandController;
46040 })(directives = app.directives || (app.directives = {}));
46041 })(app || (app = {}));
46045 (function (directives) {
46046 var HeaderMenu = (function () {
46047 function HeaderMenu() {
46048 this.restrict = 'E';
46049 this.replace = true;
46050 this.templateUrl = 'templates/header-menu.html';
46051 this.controller = 'HeaderMenuController';
46052 this.controllerAs = 'hmc';
46055 HeaderMenu.Factory = function () {
46056 var directive = function () {
46057 return new HeaderMenu();
46063 directives.HeaderMenu = HeaderMenu;
46064 var HeaderMenuController = (function () {
46065 function HeaderMenuController($state) {
46066 this.$state = $state;
46067 this.isExecution = this.$state.current.name === 'execution';
46068 this.isWorkspace = this.$state.current.name === 'workspace';
46069 this.isHistory = this.$state.current.name === 'history';
46071 HeaderMenuController.prototype.transit = function (state) {
46072 this.$state.go(state);
46074 HeaderMenuController.$inject = ['$state'];
46075 return HeaderMenuController;
46077 directives.HeaderMenuController = HeaderMenuController;
46078 })(directives = app.directives || (app.directives = {}));
46079 })(app || (app = {}));
46083 (function (directives) {
46084 var Option = (function () {
46085 function Option() {
46086 this.restrict = 'E';
46087 this.replace = true;
46088 this.controller = 'optionController';
46089 this.bindToController = {
46094 this.templateUrl = 'templates/option.html';
46095 this.controllerAs = 'ctrl';
46097 Option.Factory = function () {
46098 var directive = function () {
46099 return new Option();
46101 directive.$inject = [];
46106 directives.Option = Option;
46107 var OptionController = (function () {
46108 function OptionController() {
46109 var controller = this;
46110 angular.forEach(controller.info.arg, function (arg) {
46111 if (arg.initialValue) {
46112 if (arg.formType === 'number') {
46113 arg.input = parseInt(arg.initialValue);
46116 arg.input = arg.initialValue;
46121 OptionController.$inject = [];
46122 return OptionController;
46124 directives.OptionController = OptionController;
46125 })(directives = app.directives || (app.directives = {}));
46126 })(app || (app = {}));
46130 (function (directives) {
46131 var Directory = (function () {
46132 function Directory() {
46133 this.restrict = 'E';
46134 this.replace = true;
46135 this.controller = 'directoryController';
46136 this.controllerAs = 'ctrl';
46137 this.bindToController = {
46143 this.templateUrl = 'templates/directory.html';
46145 Directory.Factory = function () {
46146 var directive = function () {
46147 return new Directory();
46153 directives.Directory = Directory;
46154 var DirectoryController = (function () {
46155 function DirectoryController(APIEndPoint, $scope) {
46156 this.APIEndPoint = APIEndPoint;
46157 this.$scope = $scope;
46158 var controller = this;
46160 .getFiles(this.info.fileId)
46162 .then(function (result) {
46163 if (result.status === 'success') {
46164 controller.files = result.info;
46165 angular.forEach(result.info, function (file) {
46166 if (file.fileType === '0') {
46168 if (controller.info.path === '/') {
46169 o.path = '/' + file.name;
46172 o.path = controller.info.path + '/' + file.name;
46174 controller.add()(o, controller.list);
46181 DirectoryController.$inject = ['APIEndPoint', '$scope'];
46182 return DirectoryController;
46184 directives.DirectoryController = DirectoryController;
46185 })(directives = app.directives || (app.directives = {}));
46186 })(app || (app = {}));
46190 (function (directives) {
46191 var Upload = (function () {
46192 function Upload() {
46193 this.restrict = 'E';
46194 this.replace = true;
46196 this.controller = 'UploadController';
46197 this.controllerAs = 'ctrl';
46198 this.bindToController = {
46204 this.templateUrl = 'templates/upload.html';
46205 console.log("templates/upload.html-constructor");
46207 Upload.Factory = function () {
46208 var directive = function () {
46209 return new Upload();
46211 directive.$inject = [];
46216 directives.Upload = Upload;
46217 var UploadController = (function () {
46218 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
46219 this.APIEndPoint = APIEndPoint;
46220 this.$scope = $scope;
46221 this.MyModal = MyModal;
46222 this.WebSocket = WebSocket;
46223 this.$window = $window;
46224 this.$rootScope = $rootScope;
46225 this.Console = Console;
46226 var controller = this;
46227 console.log("directive.upload-constructor");
46229 UploadController.prototype.submit = function () {
46230 console.log("submit: function not supported¥n");
46232 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
46233 return UploadController;
46235 directives.UploadController = UploadController;
46236 })(directives = app.directives || (app.directives = {}));
46237 })(app || (app = {}));
46241 (function (controllers) {
46242 var Execution = (function () {
46243 function Execution(MyModal, $scope) {
46244 this.MyModal = MyModal;
46245 this.$scope = $scope;
46246 this.commandInfoList = [];
46249 Execution.prototype.add = function () {
46250 this.$scope.$broadcast('close');
46251 var commandInfoList = this.commandInfoList;
46252 var commandInstance = this.MyModal.selectCommand();
46255 .then(function (command) {
46256 commandInfoList.push(new app.declares.CommandInfo(command));
46259 Execution.prototype.open = function () {
46260 var result = this.MyModal.open('SelectCommand');
46261 console.log(result);
46263 Execution.prototype.remove = function (index, list) {
46264 list.splice(index, 1);
46266 Execution.prototype.close = function () {
46267 console.log("close");
46269 Execution.$inject = ['MyModal', '$scope'];
46272 controllers.Execution = Execution;
46273 })(controllers = app.controllers || (app.controllers = {}));
46274 })(app || (app = {}));
46278 (function (controllers) {
46279 var Workspace = (function () {
46280 function Workspace($scope, APIEndPoint, MyModal) {
46281 this.$scope = $scope;
46282 this.APIEndPoint = APIEndPoint;
46283 this.MyModal = MyModal;
46284 this.directoryList = [];
46285 var controller = this;
46286 var directoryList = this.directoryList;
46288 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
46296 directoryList.push(o);
46298 Workspace.prototype.addDirectory = function (info, directoryList) {
46299 directoryList.push(info);
46301 Workspace.prototype.upload = function () {
46302 this.MyModal.upload();
46304 Workspace.prototype.debug = function () {
46305 this.MyModal.preview();
46307 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
46310 controllers.Workspace = Workspace;
46311 })(controllers = app.controllers || (app.controllers = {}));
46312 })(app || (app = {}));
46316 (function (controllers) {
46317 var History = (function () {
46318 function History($scope) {
46319 this.page = "History";
46321 History.$inject = ['$scope'];
46324 controllers.History = History;
46325 })(controllers = app.controllers || (app.controllers = {}));
46326 })(app || (app = {}));
46330 (function (controllers) {
46331 var SelectCommand = (function () {
46332 function SelectCommand($scope, APIEndPoint, $modalInstance) {
46333 this.APIEndPoint = APIEndPoint;
46334 this.$modalInstance = $modalInstance;
46335 var controller = this;
46338 .$promise.then(function (result) {
46339 controller.tags = result.info;
46343 .$promise.then(function (result) {
46344 controller.commands = result.info;
46346 this.currentTag = 'all';
46348 SelectCommand.prototype.changeTag = function (tag) {
46349 this.currentTag = tag;
46351 SelectCommand.prototype.selectCommand = function (command) {
46352 this.$modalInstance.close(command);
46354 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
46355 return SelectCommand;
46357 controllers.SelectCommand = SelectCommand;
46358 })(controllers = app.controllers || (app.controllers = {}));
46359 })(app || (app = {}));
46363 (function (controllers) {
46364 var Upload = (function () {
46365 function Upload($scope, APIEndPoint, $modalInstance) {
46366 this.APIEndPoint = APIEndPoint;
46367 this.$modalInstance = $modalInstance;
46368 var controller = this;
46369 console.log('controller.upload-controllers');
46371 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
46374 controllers.Upload = Upload;
46375 })(controllers = app.controllers || (app.controllers = {}));
46376 })(app || (app = {}));
46380 (function (controllers) {
46381 var Preview = (function () {
46382 function Preview($scope, APIEndPoint, $modalInstance) {
46383 this.APIEndPoint = APIEndPoint;
46384 this.$modalInstance = $modalInstance;
46385 var controller = this;
46386 console.log('preview');
46388 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
46391 controllers.Preview = Preview;
46392 })(controllers = app.controllers || (app.controllers = {}));
46393 })(app || (app = {}));
46395 (function (filters) {
46397 return function (commands, tag) {
46399 angular.forEach(commands, function (command) {
46401 angular.forEach(command.tags, function (value) {
46406 result.push(command);
46412 })(filters || (filters = {}));
46416 var appName = 'zephyr';
46417 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
46418 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
46419 $urlRouterProvider.otherwise('/execution');
46420 $locationProvider.html5Mode({
46425 .state('execution', {
46427 templateUrl: 'templates/execution.html',
46428 controller: 'executionController',
46431 .state('workspace', {
46433 templateUrl: 'templates/workspace.html',
46434 controller: 'workspaceController',
46437 .state('history', {
46439 templateUrl: 'templates/history.html',
46440 controller: 'historyController',
46444 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
46445 app.zephyr.service('MyModal', app.services.MyModal);
46446 app.zephyr.service('WebSocket', app.services.WebSocket);
46447 app.zephyr.service('Console', app.services.Console);
46448 app.zephyr.filter('Tag', filters.Tag);
46449 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
46450 app.zephyr.controller('previewController', app.controllers.Preview);
46451 app.zephyr.controller('uploadController', app.controllers.Upload);
46452 app.zephyr.controller('executionController', app.controllers.Execution);
46453 app.zephyr.controller('workspaceController', app.controllers.Workspace);
46454 app.zephyr.controller('historyController', app.controllers.History);
46455 app.zephyr.controller('commandController', app.directives.CommandController);
46456 app.zephyr.controller('optionController', app.directives.OptionController);
46457 app.zephyr.controller('directoryController', app.directives.DirectoryController);
46458 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
46459 app.zephyr.controller('uploadController', app.directives.UploadController);
46460 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
46461 app.zephyr.directive('command', app.directives.Command.Factory());
46462 app.zephyr.directive('option', app.directives.Option.Factory());
46463 app.zephyr.directive('directory', app.directives.Directory.Factory());
46464 })(app || (app = {}));
46470 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
46472 link: function(scope, element, attrs) {
46473 scope.templateUrl = attrs.templateUrl;
46475 scope.isOpen = function() {
46476 var isDropdownOpen = scope.matches.length > 0;
46477 scope.assignIsOpen({ isOpen: isDropdownOpen });
46478 return isDropdownOpen;
46481 scope.isActive = function(matchIdx) {
46482 return scope.active === matchIdx;
46485 scope.selectActive = function(matchIdx) {
46486 scope.active = matchIdx;
46489 scope.selectMatch = function(activeIdx, evt) {
46490 var debounce = scope.debounce();
46491 if (angular.isNumber(debounce) || angular.isObject(debounce)) {
46492 $$debounce(function() {
46493 scope.select({activeIdx: activeIdx, evt: evt});
46494 }, angular.isNumber(debounce) ? debounce : debounce['default']);
46496 scope.select({activeIdx: activeIdx, evt: evt});
46503 .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
46510 link: function(scope, element, attrs) {
46511 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
46512 $templateRequest(tplUrl).then(function(tplContent) {
46513 var tplEl = angular.element(tplContent.trim());
46514 element.replaceWith(tplEl);
46515 $compile(tplEl)(scope);
46521 .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
46522 var isSanitizePresent;
46523 isSanitizePresent = $injector.has('$sanitize');
46525 function escapeRegexp(queryToEscape) {
46526 // Regex: capture the whole query string and replace it with the string that will be used to match
46527 // the results, for example if the capture is "a" the result will be \a
46528 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
46531 function containsHtml(matchItem) {
46532 return /<.*>/g.test(matchItem);
46535 return function(matchItem, query) {
46536 if (!isSanitizePresent && containsHtml(matchItem)) {
46537 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
46539 matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
46540 if (!isSanitizePresent) {
46541 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
46547 angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
46548 $templateCache.put("uib/template/accordion/accordion-group.html",
46549 "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
46550 " <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
46551 " <h4 class=\"panel-title\">\n" +
46552 " <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" +
46555 " <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
46556 " <div class=\"panel-body\" ng-transclude></div>\n" +
46562 angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
46563 $templateCache.put("uib/template/accordion/accordion.html",
46564 "<div class=\"panel-group\" ng-transclude></div>");
46567 angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
46568 $templateCache.put("uib/template/alert/alert.html",
46569 "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
46570 " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
46571 " <span aria-hidden=\"true\">×</span>\n" +
46572 " <span class=\"sr-only\">Close</span>\n" +
46574 " <div ng-transclude></div>\n" +
46579 angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
46580 $templateCache.put("uib/template/carousel/carousel.html",
46581 "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
46582 " <div class=\"carousel-inner\" ng-transclude></div>\n" +
46583 " <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" +
46584 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
46585 " <span class=\"sr-only\">previous</span>\n" +
46587 " <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" +
46588 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
46589 " <span class=\"sr-only\">next</span>\n" +
46591 " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
46592 " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
46593 " <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
46599 angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
46600 $templateCache.put("uib/template/carousel/slide.html",
46601 "<div ng-class=\"{\n" +
46602 " 'active': active\n" +
46603 " }\" class=\"item text-center\" ng-transclude></div>\n" +
46607 angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
46608 $templateCache.put("uib/template/datepicker/datepicker.html",
46609 "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
46610 " <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
46611 " <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
46612 " <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
46616 angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
46617 $templateCache.put("uib/template/datepicker/day.html",
46618 "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
46621 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
46622 " <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
46623 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
46626 " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
46627 " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
46631 " <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
46632 " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
46633 " <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
46634 " id=\"{{::dt.uid}}\"\n" +
46635 " ng-class=\"::dt.customClass\">\n" +
46636 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" +
46637 " uib-is-class=\"\n" +
46638 " 'btn-info' for selectedDt,\n" +
46639 " 'active' for activeDt\n" +
46641 " ng-click=\"select(dt.date)\"\n" +
46642 " ng-disabled=\"::dt.disabled\"\n" +
46643 " tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
46651 angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
46652 $templateCache.put("uib/template/datepicker/month.html",
46653 "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
46656 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
46657 " <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
46658 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
46662 " <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
46663 " <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
46664 " id=\"{{::dt.uid}}\"\n" +
46665 " ng-class=\"::dt.customClass\">\n" +
46666 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
46667 " uib-is-class=\"\n" +
46668 " 'btn-info' for selectedDt,\n" +
46669 " 'active' for activeDt\n" +
46671 " ng-click=\"select(dt.date)\"\n" +
46672 " ng-disabled=\"::dt.disabled\"\n" +
46673 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
46681 angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
46682 $templateCache.put("uib/template/datepicker/popup.html",
46683 "<ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
46684 " <li ng-transclude></li>\n" +
46685 " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" +
46686 " <span class=\"btn-group pull-left\">\n" +
46687 " <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
46688 " <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
46690 " <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
46696 angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
46697 $templateCache.put("uib/template/datepicker/year.html",
46698 "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
46701 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
46702 " <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
46703 " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
46707 " <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
46708 " <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
46709 " id=\"{{::dt.uid}}\"\n" +
46710 " ng-class=\"::dt.customClass\">\n" +
46711 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
46712 " uib-is-class=\"\n" +
46713 " 'btn-info' for selectedDt,\n" +
46714 " 'active' for activeDt\n" +
46716 " ng-click=\"select(dt.date)\"\n" +
46717 " ng-disabled=\"::dt.disabled\"\n" +
46718 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
46726 angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
46727 $templateCache.put("uib/template/modal/backdrop.html",
46728 "<div class=\"modal-backdrop\"\n" +
46729 " uib-modal-animation-class=\"fade\"\n" +
46730 " modal-in-class=\"in\"\n" +
46731 " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
46736 angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
46737 $templateCache.put("uib/template/modal/window.html",
46738 "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
46739 " uib-modal-animation-class=\"fade\"\n" +
46740 " modal-in-class=\"in\"\n" +
46741 " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
46742 " <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
46747 angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
46748 $templateCache.put("uib/template/pager/pager.html",
46749 "<ul class=\"pager\">\n" +
46750 " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
46751 " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
46756 angular.module("uib/template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
46757 $templateCache.put("uib/template/pagination/pager.html",
46758 "<ul class=\"pager\">\n" +
46759 " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
46760 " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
46765 angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
46766 $templateCache.put("uib/template/pagination/pagination.html",
46767 "<ul class=\"pagination\">\n" +
46768 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
46769 " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
46770 " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
46771 " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
46772 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
46777 angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
46778 $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
46779 "<div class=\"tooltip\"\n" +
46780 " tooltip-animation-class=\"fade\"\n" +
46781 " uib-tooltip-classes\n" +
46782 " ng-class=\"{ in: isOpen() }\">\n" +
46783 " <div class=\"tooltip-arrow\"></div>\n" +
46784 " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
46789 angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
46790 $templateCache.put("uib/template/tooltip/tooltip-popup.html",
46791 "<div class=\"tooltip\"\n" +
46792 " tooltip-animation-class=\"fade\"\n" +
46793 " uib-tooltip-classes\n" +
46794 " ng-class=\"{ in: isOpen() }\">\n" +
46795 " <div class=\"tooltip-arrow\"></div>\n" +
46796 " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
46801 angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
46802 $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
46803 "<div class=\"tooltip\"\n" +
46804 " tooltip-animation-class=\"fade\"\n" +
46805 " uib-tooltip-classes\n" +
46806 " ng-class=\"{ in: isOpen() }\">\n" +
46807 " <div class=\"tooltip-arrow\"></div>\n" +
46808 " <div class=\"tooltip-inner\"\n" +
46809 " uib-tooltip-template-transclude=\"contentExp()\"\n" +
46810 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
46815 angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
46816 $templateCache.put("uib/template/popover/popover-html.html",
46817 "<div class=\"popover\"\n" +
46818 " tooltip-animation-class=\"fade\"\n" +
46819 " uib-tooltip-classes\n" +
46820 " ng-class=\"{ in: isOpen() }\">\n" +
46821 " <div class=\"arrow\"></div>\n" +
46823 " <div class=\"popover-inner\">\n" +
46824 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
46825 " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
46831 angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
46832 $templateCache.put("uib/template/popover/popover-template.html",
46833 "<div class=\"popover\"\n" +
46834 " tooltip-animation-class=\"fade\"\n" +
46835 " uib-tooltip-classes\n" +
46836 " ng-class=\"{ in: isOpen() }\">\n" +
46837 " <div class=\"arrow\"></div>\n" +
46839 " <div class=\"popover-inner\">\n" +
46840 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
46841 " <div class=\"popover-content\"\n" +
46842 " uib-tooltip-template-transclude=\"contentExp()\"\n" +
46843 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
46849 angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
46850 $templateCache.put("uib/template/popover/popover.html",
46851 "<div class=\"popover\"\n" +
46852 " tooltip-animation-class=\"fade\"\n" +
46853 " uib-tooltip-classes\n" +
46854 " ng-class=\"{ in: isOpen() }\">\n" +
46855 " <div class=\"arrow\"></div>\n" +
46857 " <div class=\"popover-inner\">\n" +
46858 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
46859 " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
46865 angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
46866 $templateCache.put("uib/template/progressbar/bar.html",
46867 "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
46871 angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
46872 $templateCache.put("uib/template/progressbar/progress.html",
46873 "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
46876 angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
46877 $templateCache.put("uib/template/progressbar/progressbar.html",
46878 "<div class=\"progress\">\n" +
46879 " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
46884 angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
46885 $templateCache.put("uib/template/rating/rating.html",
46886 "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
46887 " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
46888 " <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\" aria-valuetext=\"{{r.title}}\"></i>\n" +
46893 angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
46894 $templateCache.put("uib/template/tabs/tab.html",
46895 "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" +
46896 " <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" +
46901 angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
46902 $templateCache.put("uib/template/tabs/tabset.html",
46904 " <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
46905 " <div class=\"tab-content\">\n" +
46906 " <div class=\"tab-pane\" \n" +
46907 " ng-repeat=\"tab in tabs\" \n" +
46908 " ng-class=\"{active: tab.active}\"\n" +
46909 " uib-tab-content-transclude=\"tab\">\n" +
46916 angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
46917 $templateCache.put("uib/template/timepicker/timepicker.html",
46918 "<table class=\"uib-timepicker\">\n" +
46920 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
46921 " <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
46922 " <td> </td>\n" +
46923 " <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
46924 " <td ng-show=\"showSeconds\"> </td>\n" +
46925 " <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
46926 " <td ng-show=\"showMeridian\"></td>\n" +
46929 " <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
46930 " <input style=\"width:50px;\" type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
46932 " <td class=\"uib-separator\">:</td>\n" +
46933 " <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
46934 " <input style=\"width:50px;\" type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
46936 " <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
46937 " <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
46938 " <input style=\"width:50px;\" type=\"text\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
46940 " <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
46942 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
46943 " <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
46944 " <td> </td>\n" +
46945 " <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
46946 " <td ng-show=\"showSeconds\"> </td>\n" +
46947 " <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
46948 " <td ng-show=\"showMeridian\"></td>\n" +
46955 angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
46956 $templateCache.put("uib/template/typeahead/typeahead-match.html",
46957 "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" +
46961 angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
46962 $templateCache.put("uib/template/typeahead/typeahead-popup.html",
46963 "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
46964 " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
46965 " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
46970 angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); })
46974 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
46975 /***/ function(module, exports) {
46980 (function (declares) {
46981 var CommandInfo = (function () {
46982 function CommandInfo(name) {
46985 return CommandInfo;
46987 declares.CommandInfo = CommandInfo;
46988 })(declares = app.declares || (app.declares = {}));
46989 })(app || (app = {}));
46993 (function (services) {
46994 var APIEndPoint = (function () {
46995 function APIEndPoint($resource, $http) {
46996 this.$resource = $resource;
46997 this.$http = $http;
46999 APIEndPoint.prototype.resource = function (endPoint, data) {
47000 var customAction = {
47006 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
47008 return this.$resource(endPoint, {}, { execute: execute });
47010 APIEndPoint.prototype.getOptionControlFile = function (command) {
47011 var endPoint = '/api/v1/optionControlFile/' + command;
47012 return this.resource(endPoint, {}).get();
47014 APIEndPoint.prototype.getFiles = function (fileId) {
47015 var endPoint = '/api/v1/workspace';
47017 endPoint += '/' + fileId;
47019 return this.resource(endPoint, {}).get();
47021 APIEndPoint.prototype.getDirectories = function () {
47022 var endPoint = '/api/v1/all/workspace/directory';
47023 return this.resource(endPoint, {}).get();
47025 APIEndPoint.prototype.getTags = function () {
47026 var endPoint = '/api/v1/tagList';
47027 return this.resource(endPoint, {}).get();
47029 APIEndPoint.prototype.getCommands = function () {
47030 var endPoint = '/api/v1/commandList';
47031 return this.resource(endPoint, {}).get();
47033 APIEndPoint.prototype.execute = function (data) {
47034 var endPoint = '/api/v1/execution';
47035 var fd = new FormData();
47036 fd.append('data', data);
47037 return this.$http.post(endPoint, fd, {
47038 headers: { 'Content-Type': undefined },
47039 transformRequest: angular.identity
47042 APIEndPoint.prototype.debug = function () {
47043 var endPoint = '/api/v1/debug';
47044 return this.$http.get(endPoint);
47047 APIEndPoint.prototype.upload = function () {
47048 var endPoint = '/api/v1/upload';
47049 return this.$http.get(endPoint);
47052 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47053 APIEndPoint.prototype.help = function (command) {
47054 var endPoint = '/api/v1/help/' + command;
47055 return this.$http.get(endPoint);
47057 return APIEndPoint;
47059 services.APIEndPoint = APIEndPoint;
47060 })(services = app.services || (app.services = {}));
47061 })(app || (app = {}));
47065 (function (services) {
47066 var MyModal = (function () {
47067 function MyModal($uibModal) {
47068 this.$uibModal = $uibModal;
47069 this.modalOption = {
47076 MyModal.prototype.open = function (modalName) {
47077 if (modalName === 'SelectCommand') {
47078 this.modalOption.templateUrl = 'templates/select-command.html';
47079 this.modalOption.size = 'lg';
47081 return this.$uibModal.open(this.modalOption);
47083 MyModal.prototype.selectCommand = function () {
47084 this.modalOption.templateUrl = 'templates/select-command.html';
47085 this.modalOption.controller = 'selectCommandController';
47086 this.modalOption.controllerAs = 'c';
47087 this.modalOption.size = 'lg';
47088 return this.$uibModal.open(this.modalOption);
47090 MyModal.prototype.preview = function () {
47091 this.modalOption.templateUrl = 'templates/preview.html';
47092 this.modalOption.controller = 'previewController';
47093 this.modalOption.controllerAs = 'c';
47094 this.modalOption.size = 'lg';
47095 return this.$uibModal.open(this.modalOption);
47098 MyModal.prototype.upload = function () {
47099 this.modalOption.templateUrl = 'templates/upload.html';
47100 this.modalOption.controller = 'uploadController';
47101 this.modalOption.controllerAs = 'c';
47102 this.modalOption.size = 'lg';
47103 return this.$uibModal.open(this.modalOption);
47106 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47107 MyModal.$inject = ['$uibModal'];
47110 services.MyModal = MyModal;
47111 })(services = app.services || (app.services = {}));
47112 })(app || (app = {}));
47116 (function (services) {
47117 var WebSocket = (function () {
47118 function WebSocket($rootScope) {
47119 this.$rootScope = $rootScope;
47120 this.socket = io.connect();
47122 WebSocket.prototype.on = function (eventName, callback) {
47123 var socket = this.socket;
47124 var rootScope = this.$rootScope;
47125 socket.on(eventName, function () {
47126 var args = arguments;
47127 rootScope.$apply(function () {
47128 callback.apply(socket, args);
47132 WebSocket.prototype.emit = function (eventName, data, callback) {
47133 var socket = this.socket;
47134 var rootScope = this.$rootScope;
47135 this.socket.emit(eventName, data, function () {
47136 var args = arguments;
47137 rootScope.$apply(function () {
47139 callback.apply(socket, args);
47145 services.WebSocket = WebSocket;
47146 })(services = app.services || (app.services = {}));
47147 })(app || (app = {}));
47151 (function (services) {
47152 var Console = (function () {
47153 function Console(WebSocket, $rootScope) {
47154 this.WebSocket = WebSocket;
47155 this.$rootScope = $rootScope;
47156 this.WebSocket = WebSocket;
47157 this.$rootScope = $rootScope;
47158 this.directiveIDs = [];
47159 var directiveIDs = this.directiveIDs;
47160 this.WebSocket.on('console', function (d) {
47162 var message = d.message;
47163 if (directiveIDs.indexOf(id) > -1) {
47164 $rootScope.$emit(id, message);
47168 Console.prototype.addDirective = function (id) {
47169 if (!(this.directiveIDs.indexOf(id) > -1)) {
47170 this.directiveIDs.push(id);
47173 Console.prototype.removeDirective = function (id) {
47174 var i = this.directiveIDs.indexOf(id);
47176 this.directiveIDs.splice(i, 1);
47179 Console.prototype.showIDs = function () {
47180 console.log(this.directiveIDs);
47184 services.Console = Console;
47185 })(services = app.services || (app.services = {}));
47186 })(app || (app = {}));
47190 (function (directives) {
47191 var Command = (function () {
47192 function Command() {
47193 this.restrict = 'E';
47194 this.replace = true;
47196 this.controller = 'commandController';
47197 this.controllerAs = 'ctrl';
47198 this.bindToController = {
47204 this.templateUrl = 'templates/command.html';
47206 Command.Factory = function () {
47207 var directive = function () {
47208 return new Command();
47210 directive.$inject = [];
47215 directives.Command = Command;
47216 var CommandController = (function () {
47217 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
47218 this.APIEndPoint = APIEndPoint;
47219 this.$scope = $scope;
47220 this.MyModal = MyModal;
47221 this.WebSocket = WebSocket;
47222 this.$window = $window;
47223 this.$rootScope = $rootScope;
47224 this.Console = Console;
47225 var controller = this;
47227 .getOptionControlFile(this.name)
47229 .then(function (result) {
47230 controller.options = result.info;
47235 .then(function (result) {
47236 controller.dirs = result.info;
47238 this.heading = "[" + this.index + "]: dcdFilePrint";
47239 this.isOpen = true;
47240 this.$scope.$on('close', function () {
47241 controller.isOpen = false;
47245 return Math.floor((1 + Math.random()) * 0x10000)
47249 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
47250 s4() + '-' + s4() + s4() + s4();
47252 this.uuid = guid();
47253 this.Console.addDirective(this.uuid);
47254 this.Console.showIDs();
47256 CommandController.prototype.submit = function () {
47258 angular.forEach(this.options, function (option) {
47260 name: option.option,
47263 angular.forEach(option.arg, function (arg) {
47265 if (typeof arg.input === 'object') {
47266 obj.arguments.push(arg.input.name);
47269 obj.arguments.push(arg.input);
47273 if (obj.arguments.length > 0) {
47278 command: this.name,
47279 workspace: this.workspace.fileId,
47283 .execute(JSON.stringify(execObj))
47284 .then(function (result) {
47285 console.log(result);
47288 CommandController.prototype.removeMySelf = function (index) {
47289 this.$scope.$destroy();
47290 this.Console.removeDirective(this.uuid);
47291 this.remove()(index, this.list);
47292 this.Console.showIDs();
47294 CommandController.prototype.reloadFiles = function () {
47296 var fileId = this.workspace.fileId;
47300 .then(function (result) {
47301 var status = result.status;
47302 if (status === 'success') {
47303 _this.files = result.info;
47306 console.log(result.message);
47310 CommandController.prototype.debug = function () {
47311 var div = angular.element(this.$window.document).find("div");
47314 angular.forEach(div, function (v) {
47315 if (v.className === "panel-body console") {
47318 else if (v.className === "row parameters-console") {
47322 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
47323 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
47324 consoleTag.style.height = consoleHeight;
47325 consoleTag.style.width = consoleWidth;
47327 CommandController.prototype.help = function () {
47330 .then(function (result) {
47331 console.log(result);
47334 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
47335 return CommandController;
47337 directives.CommandController = CommandController;
47338 })(directives = app.directives || (app.directives = {}));
47339 })(app || (app = {}));
47343 (function (directives) {
47344 var HeaderMenu = (function () {
47345 function HeaderMenu() {
47346 this.restrict = 'E';
47347 this.replace = true;
47348 this.templateUrl = 'templates/header-menu.html';
47349 this.controller = 'HeaderMenuController';
47350 this.controllerAs = 'hmc';
47353 HeaderMenu.Factory = function () {
47354 var directive = function () {
47355 return new HeaderMenu();
47361 directives.HeaderMenu = HeaderMenu;
47362 var HeaderMenuController = (function () {
47363 function HeaderMenuController($state) {
47364 this.$state = $state;
47365 this.isExecution = this.$state.current.name === 'execution';
47366 this.isWorkspace = this.$state.current.name === 'workspace';
47367 this.isHistory = this.$state.current.name === 'history';
47369 HeaderMenuController.prototype.transit = function (state) {
47370 this.$state.go(state);
47372 HeaderMenuController.$inject = ['$state'];
47373 return HeaderMenuController;
47375 directives.HeaderMenuController = HeaderMenuController;
47376 })(directives = app.directives || (app.directives = {}));
47377 })(app || (app = {}));
47381 (function (directives) {
47382 var Option = (function () {
47383 function Option() {
47384 this.restrict = 'E';
47385 this.replace = true;
47386 this.controller = 'optionController';
47387 this.bindToController = {
47392 this.templateUrl = 'templates/option.html';
47393 this.controllerAs = 'ctrl';
47395 Option.Factory = function () {
47396 var directive = function () {
47397 return new Option();
47399 directive.$inject = [];
47404 directives.Option = Option;
47405 var OptionController = (function () {
47406 function OptionController() {
47407 var controller = this;
47408 angular.forEach(controller.info.arg, function (arg) {
47409 if (arg.initialValue) {
47410 if (arg.formType === 'number') {
47411 arg.input = parseInt(arg.initialValue);
47414 arg.input = arg.initialValue;
47419 OptionController.$inject = [];
47420 return OptionController;
47422 directives.OptionController = OptionController;
47423 })(directives = app.directives || (app.directives = {}));
47424 })(app || (app = {}));
47428 (function (directives) {
47429 var Directory = (function () {
47430 function Directory() {
47431 this.restrict = 'E';
47432 this.replace = true;
47433 this.controller = 'directoryController';
47434 this.controllerAs = 'ctrl';
47435 this.bindToController = {
47441 this.templateUrl = 'templates/directory.html';
47443 Directory.Factory = function () {
47444 var directive = function () {
47445 return new Directory();
47451 directives.Directory = Directory;
47452 var DirectoryController = (function () {
47453 function DirectoryController(APIEndPoint, $scope) {
47454 this.APIEndPoint = APIEndPoint;
47455 this.$scope = $scope;
47456 var controller = this;
47458 .getFiles(this.info.fileId)
47460 .then(function (result) {
47461 if (result.status === 'success') {
47462 controller.files = result.info;
47463 angular.forEach(result.info, function (file) {
47464 if (file.fileType === '0') {
47466 if (controller.info.path === '/') {
47467 o.path = '/' + file.name;
47470 o.path = controller.info.path + '/' + file.name;
47472 controller.add()(o, controller.list);
47479 DirectoryController.$inject = ['APIEndPoint', '$scope'];
47480 return DirectoryController;
47482 directives.DirectoryController = DirectoryController;
47483 })(directives = app.directives || (app.directives = {}));
47484 })(app || (app = {}));
47489 (function (directives) {
47490 var Upload = (function () {
47491 function Upload() {
47492 this.restrict = 'E';
47493 this.replace = true;
47495 this.controller = 'UploadController';
47496 this.controllerAs = 'ctrl';
47497 this.bindToController = {
47503 this.templateUrl = 'templates/upload.html';
47504 console.log("templates/upload.html-constructor");
47506 Upload.Factory = function () {
47507 var directive = function () {
47508 return new Upload();
47510 directive.$inject = [];
47515 directives.Upload = Upload;
47516 var UploadController = (function () {
47517 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
47518 this.APIEndPoint = APIEndPoint;
47519 this.$scope = $scope;
47520 this.MyModal = MyModal;
47521 this.WebSocket = WebSocket;
47522 this.$window = $window;
47523 this.$rootScope = $rootScope;
47524 this.Console = Console;
47525 var controller = this;
47526 console.log("directive.upload-constructor");
47528 UploadController.prototype.submit = function () {
47529 console.log("submit: function not supported¥n");
47531 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
47532 return UploadController;
47534 directives.UploadController = UploadController;
47535 })(directives = app.directives || (app.directives = {}));
47536 })(app || (app = {}));
47540 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47542 (function (controllers) {
47543 var Execution = (function () {
47544 function Execution(MyModal, $scope) {
47545 this.MyModal = MyModal;
47546 this.$scope = $scope;
47547 this.commandInfoList = [];
47550 Execution.prototype.add = function () {
47551 this.$scope.$broadcast('close');
47552 var commandInfoList = this.commandInfoList;
47553 var commandInstance = this.MyModal.selectCommand();
47556 .then(function (command) {
47557 commandInfoList.push(new app.declares.CommandInfo(command));
47560 Execution.prototype.open = function () {
47561 var result = this.MyModal.open('SelectCommand');
47562 console.log(result);
47564 Execution.prototype.remove = function (index, list) {
47565 list.splice(index, 1);
47567 Execution.prototype.close = function () {
47568 console.log("close");
47570 Execution.$inject = ['MyModal', '$scope'];
47573 controllers.Execution = Execution;
47574 })(controllers = app.controllers || (app.controllers = {}));
47575 })(app || (app = {}));
47579 (function (controllers) {
47580 var Workspace = (function () {
47581 function Workspace($scope, APIEndPoint, MyModal) {
47582 this.$scope = $scope;
47583 this.APIEndPoint = APIEndPoint;
47584 this.MyModal = MyModal;
47585 this.directoryList = [];
47586 var controller = this;
47587 var directoryList = this.directoryList;
47589 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
47597 directoryList.push(o);
47599 Workspace.prototype.addDirectory = function (info, directoryList) {
47600 directoryList.push(info);
47603 Workspace.prototype.upload = function () {
47604 this.MyModal.upload();
47607 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47608 Workspace.prototype.debug = function () {
47609 this.MyModal.preview();
47611 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
47614 controllers.Workspace = Workspace;
47615 })(controllers = app.controllers || (app.controllers = {}));
47616 })(app || (app = {}));
47620 (function (controllers) {
47621 var History = (function () {
47622 function History($scope) {
47623 this.page = "History";
47625 History.$inject = ['$scope'];
47628 controllers.History = History;
47629 })(controllers = app.controllers || (app.controllers = {}));
47630 })(app || (app = {}));
47634 (function (controllers) {
47635 var SelectCommand = (function () {
47636 function SelectCommand($scope, APIEndPoint, $modalInstance) {
47637 this.APIEndPoint = APIEndPoint;
47638 this.$modalInstance = $modalInstance;
47639 var controller = this;
47642 .$promise.then(function (result) {
47643 controller.tags = result.info;
47647 .$promise.then(function (result) {
47648 controller.commands = result.info;
47650 this.currentTag = 'all';
47652 SelectCommand.prototype.changeTag = function (tag) {
47653 this.currentTag = tag;
47655 SelectCommand.prototype.selectCommand = function (command) {
47656 this.$modalInstance.close(command);
47658 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47659 return SelectCommand;
47661 controllers.SelectCommand = SelectCommand;
47662 })(controllers = app.controllers || (app.controllers = {}));
47663 })(app || (app = {}));
47667 (function (controllers) {
47669 var Upload = (function () {
47670 function Upload($scope, APIEndPoint, $modalInstance) {
47671 this.APIEndPoint = APIEndPoint;
47672 this.$modalInstance = $modalInstance;
47673 var controller = this;
47674 console.log('controller.upload-controllers');
47676 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47679 controllers.Upload = Upload;
47680 })(controllers = app.controllers || (app.controllers = {}));
47681 })(app || (app = {}));
47685 (function (controllers) {
47687 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47688 var Preview = (function () {
47689 function Preview($scope, APIEndPoint, $modalInstance) {
47690 this.APIEndPoint = APIEndPoint;
47691 this.$modalInstance = $modalInstance;
47692 var controller = this;
47693 console.log('preview');
47695 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47698 controllers.Preview = Preview;
47699 })(controllers = app.controllers || (app.controllers = {}));
47700 })(app || (app = {}));
47702 (function (filters) {
47704 return function (commands, tag) {
47706 angular.forEach(commands, function (command) {
47708 angular.forEach(command.tags, function (value) {
47713 result.push(command);
47719 })(filters || (filters = {}));
47723 var appName = 'zephyr';
47724 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
47725 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
47726 $urlRouterProvider.otherwise('/execution');
47727 $locationProvider.html5Mode({
47732 .state('execution', {
47734 templateUrl: 'templates/execution.html',
47735 controller: 'executionController',
47738 .state('workspace', {
47740 templateUrl: 'templates/workspace.html',
47741 controller: 'workspaceController',
47744 .state('history', {
47746 templateUrl: 'templates/history.html',
47747 controller: 'historyController',
47751 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
47752 app.zephyr.service('MyModal', app.services.MyModal);
47753 app.zephyr.service('WebSocket', app.services.WebSocket);
47754 app.zephyr.service('Console', app.services.Console);
47755 app.zephyr.filter('Tag', filters.Tag);
47756 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
47757 app.zephyr.controller('previewController', app.controllers.Preview);
47759 app.zephyr.controller('uploadController', app.controllers.Upload);
47761 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47762 app.zephyr.controller('executionController', app.controllers.Execution);
47763 app.zephyr.controller('workspaceController', app.controllers.Workspace);
47764 app.zephyr.controller('historyController', app.controllers.History);
47765 app.zephyr.controller('commandController', app.directives.CommandController);
47766 app.zephyr.controller('optionController', app.directives.OptionController);
47767 app.zephyr.controller('directoryController', app.directives.DirectoryController);
47768 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
47770 app.zephyr.controller('uploadController', app.directives.UploadController);
47772 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47773 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
47774 app.zephyr.directive('command', app.directives.Command.Factory());
47775 app.zephyr.directive('option', app.directives.Option.Factory());
47776 app.zephyr.directive('directory', app.directives.Directory.Factory());
47777 })(app || (app = {}));
47785 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47786 /***/ function(module, exports) {
47791 (function (declares) {
47792 var CommandInfo = (function () {
47793 function CommandInfo(name) {
47796 return CommandInfo;
47798 declares.CommandInfo = CommandInfo;
47799 })(declares = app.declares || (app.declares = {}));
47800 })(app || (app = {}));
47804 (function (services) {
47805 var APIEndPoint = (function () {
47806 function APIEndPoint($resource, $http) {
47807 this.$resource = $resource;
47808 this.$http = $http;
47810 APIEndPoint.prototype.resource = function (endPoint, data) {
47811 var customAction = {
47817 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
47819 return this.$resource(endPoint, {}, { execute: execute });
47821 APIEndPoint.prototype.getOptionControlFile = function (command) {
47822 var endPoint = '/api/v1/optionControlFile/' + command;
47823 return this.resource(endPoint, {}).get();
47825 APIEndPoint.prototype.getFiles = function (fileId) {
47826 var endPoint = '/api/v1/workspace';
47828 endPoint += '/' + fileId;
47830 return this.resource(endPoint, {}).get();
47832 APIEndPoint.prototype.getDirectories = function () {
47833 var endPoint = '/api/v1/all/workspace/directory';
47834 return this.resource(endPoint, {}).get();
47836 APIEndPoint.prototype.getTags = function () {
47837 var endPoint = '/api/v1/tagList';
47838 return this.resource(endPoint, {}).get();
47840 APIEndPoint.prototype.getCommands = function () {
47841 var endPoint = '/api/v1/commandList';
47842 return this.resource(endPoint, {}).get();
47844 APIEndPoint.prototype.execute = function (data) {
47845 var endPoint = '/api/v1/execution';
47846 var fd = new FormData();
47847 fd.append('data', data);
47848 return this.$http.post(endPoint, fd, {
47849 headers: { 'Content-Type': undefined },
47850 transformRequest: angular.identity
47853 APIEndPoint.prototype.debug = function () {
47854 var endPoint = '/api/v1/debug';
47855 return this.$http.get(endPoint);
47858 APIEndPoint.prototype.upload = function () {
47859 var endPoint = '/api/v1/upload';
47860 return this.$http.get(endPoint);
47863 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47864 APIEndPoint.prototype.help = function (command) {
47865 var endPoint = '/api/v1/help/' + command;
47866 return this.$http.get(endPoint);
47868 return APIEndPoint;
47870 services.APIEndPoint = APIEndPoint;
47871 })(services = app.services || (app.services = {}));
47872 })(app || (app = {}));
47876 (function (services) {
47877 var MyModal = (function () {
47878 function MyModal($uibModal) {
47879 this.$uibModal = $uibModal;
47880 this.modalOption = {
47887 MyModal.prototype.open = function (modalName) {
47888 if (modalName === 'SelectCommand') {
47889 this.modalOption.templateUrl = 'templates/select-command.html';
47890 this.modalOption.size = 'lg';
47892 return this.$uibModal.open(this.modalOption);
47894 MyModal.prototype.selectCommand = function () {
47895 this.modalOption.templateUrl = 'templates/select-command.html';
47896 this.modalOption.controller = 'selectCommandController';
47897 this.modalOption.controllerAs = 'c';
47898 this.modalOption.size = 'lg';
47899 return this.$uibModal.open(this.modalOption);
47901 MyModal.prototype.preview = function () {
47902 this.modalOption.templateUrl = 'templates/preview.html';
47903 this.modalOption.controller = 'previewController';
47904 this.modalOption.controllerAs = 'c';
47905 this.modalOption.size = 'lg';
47906 return this.$uibModal.open(this.modalOption);
47909 MyModal.prototype.upload = function () {
47910 this.modalOption.templateUrl = 'templates/upload.html';
47911 this.modalOption.controller = 'uploadController';
47912 this.modalOption.controllerAs = 'c';
47913 this.modalOption.size = 'lg';
47914 return this.$uibModal.open(this.modalOption);
47917 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
47918 MyModal.$inject = ['$uibModal'];
47921 services.MyModal = MyModal;
47922 })(services = app.services || (app.services = {}));
47923 })(app || (app = {}));
47927 (function (services) {
47928 var WebSocket = (function () {
47929 function WebSocket($rootScope) {
47930 this.$rootScope = $rootScope;
47931 this.socket = io.connect();
47933 WebSocket.prototype.on = function (eventName, callback) {
47934 var socket = this.socket;
47935 var rootScope = this.$rootScope;
47936 socket.on(eventName, function () {
47937 var args = arguments;
47938 rootScope.$apply(function () {
47939 callback.apply(socket, args);
47943 WebSocket.prototype.emit = function (eventName, data, callback) {
47944 var socket = this.socket;
47945 var rootScope = this.$rootScope;
47946 this.socket.emit(eventName, data, function () {
47947 var args = arguments;
47948 rootScope.$apply(function () {
47950 callback.apply(socket, args);
47956 services.WebSocket = WebSocket;
47957 })(services = app.services || (app.services = {}));
47958 })(app || (app = {}));
47962 (function (services) {
47963 var Console = (function () {
47964 function Console(WebSocket, $rootScope) {
47965 this.WebSocket = WebSocket;
47966 this.$rootScope = $rootScope;
47967 this.WebSocket = WebSocket;
47968 this.$rootScope = $rootScope;
47969 this.directiveIDs = [];
47970 var directiveIDs = this.directiveIDs;
47971 this.WebSocket.on('console', function (d) {
47973 var message = d.message;
47974 if (directiveIDs.indexOf(id) > -1) {
47975 $rootScope.$emit(id, message);
47979 Console.prototype.addDirective = function (id) {
47980 if (!(this.directiveIDs.indexOf(id) > -1)) {
47981 this.directiveIDs.push(id);
47984 Console.prototype.removeDirective = function (id) {
47985 var i = this.directiveIDs.indexOf(id);
47987 this.directiveIDs.splice(i, 1);
47990 Console.prototype.showIDs = function () {
47991 console.log(this.directiveIDs);
47995 services.Console = Console;
47996 })(services = app.services || (app.services = {}));
47997 })(app || (app = {}));
48001 (function (directives) {
48002 var Command = (function () {
48003 function Command() {
48004 this.restrict = 'E';
48005 this.replace = true;
48007 this.controller = 'commandController';
48008 this.controllerAs = 'ctrl';
48009 this.bindToController = {
48015 this.templateUrl = 'templates/command.html';
48017 Command.Factory = function () {
48018 var directive = function () {
48019 return new Command();
48021 directive.$inject = [];
48026 directives.Command = Command;
48027 var CommandController = (function () {
48028 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
48029 this.APIEndPoint = APIEndPoint;
48030 this.$scope = $scope;
48031 this.MyModal = MyModal;
48032 this.WebSocket = WebSocket;
48033 this.$window = $window;
48034 this.$rootScope = $rootScope;
48035 this.Console = Console;
48036 var controller = this;
48038 .getOptionControlFile(this.name)
48040 .then(function (result) {
48041 controller.options = result.info;
48046 .then(function (result) {
48047 controller.dirs = result.info;
48049 this.heading = "[" + this.index + "]: dcdFilePrint";
48050 this.isOpen = true;
48051 this.$scope.$on('close', function () {
48052 controller.isOpen = false;
48056 return Math.floor((1 + Math.random()) * 0x10000)
48060 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
48061 s4() + '-' + s4() + s4() + s4();
48063 this.uuid = guid();
48064 this.Console.addDirective(this.uuid);
48065 this.Console.showIDs();
48067 CommandController.prototype.submit = function () {
48069 angular.forEach(this.options, function (option) {
48071 name: option.option,
48074 angular.forEach(option.arg, function (arg) {
48076 if (typeof arg.input === 'object') {
48077 obj.arguments.push(arg.input.name);
48080 obj.arguments.push(arg.input);
48084 if (obj.arguments.length > 0) {
48089 command: this.name,
48090 workspace: this.workspace.fileId,
48094 .execute(JSON.stringify(execObj))
48095 .then(function (result) {
48096 console.log(result);
48099 CommandController.prototype.removeMySelf = function (index) {
48100 this.$scope.$destroy();
48101 this.Console.removeDirective(this.uuid);
48102 this.remove()(index, this.list);
48103 this.Console.showIDs();
48105 CommandController.prototype.reloadFiles = function () {
48107 var fileId = this.workspace.fileId;
48111 .then(function (result) {
48112 var status = result.status;
48113 if (status === 'success') {
48114 _this.files = result.info;
48117 console.log(result.message);
48121 CommandController.prototype.debug = function () {
48122 var div = angular.element(this.$window.document).find("div");
48125 angular.forEach(div, function (v) {
48126 if (v.className === "panel-body console") {
48129 else if (v.className === "row parameters-console") {
48133 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
48134 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
48135 consoleTag.style.height = consoleHeight;
48136 consoleTag.style.width = consoleWidth;
48138 CommandController.prototype.help = function () {
48141 .then(function (result) {
48142 console.log(result);
48145 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
48146 return CommandController;
48148 directives.CommandController = CommandController;
48149 })(directives = app.directives || (app.directives = {}));
48150 })(app || (app = {}));
48154 (function (directives) {
48155 var HeaderMenu = (function () {
48156 function HeaderMenu() {
48157 this.restrict = 'E';
48158 this.replace = true;
48159 this.templateUrl = 'templates/header-menu.html';
48160 this.controller = 'HeaderMenuController';
48161 this.controllerAs = 'hmc';
48164 HeaderMenu.Factory = function () {
48165 var directive = function () {
48166 return new HeaderMenu();
48172 directives.HeaderMenu = HeaderMenu;
48173 var HeaderMenuController = (function () {
48174 function HeaderMenuController($state) {
48175 this.$state = $state;
48176 this.isExecution = this.$state.current.name === 'execution';
48177 this.isWorkspace = this.$state.current.name === 'workspace';
48178 this.isHistory = this.$state.current.name === 'history';
48180 HeaderMenuController.prototype.transit = function (state) {
48181 this.$state.go(state);
48183 HeaderMenuController.$inject = ['$state'];
48184 return HeaderMenuController;
48186 directives.HeaderMenuController = HeaderMenuController;
48187 })(directives = app.directives || (app.directives = {}));
48188 })(app || (app = {}));
48192 (function (directives) {
48193 var Option = (function () {
48194 function Option() {
48195 this.restrict = 'E';
48196 this.replace = true;
48197 this.controller = 'optionController';
48198 this.bindToController = {
48203 this.templateUrl = 'templates/option.html';
48204 this.controllerAs = 'ctrl';
48206 Option.Factory = function () {
48207 var directive = function () {
48208 return new Option();
48210 directive.$inject = [];
48215 directives.Option = Option;
48216 var OptionController = (function () {
48217 function OptionController() {
48218 var controller = this;
48219 angular.forEach(controller.info.arg, function (arg) {
48220 if (arg.initialValue) {
48221 if (arg.formType === 'number') {
48222 arg.input = parseInt(arg.initialValue);
48225 arg.input = arg.initialValue;
48230 OptionController.$inject = [];
48231 return OptionController;
48233 directives.OptionController = OptionController;
48234 })(directives = app.directives || (app.directives = {}));
48235 })(app || (app = {}));
48239 (function (directives) {
48240 var Directory = (function () {
48241 function Directory() {
48242 this.restrict = 'E';
48243 this.replace = true;
48244 this.controller = 'directoryController';
48245 this.controllerAs = 'ctrl';
48246 this.bindToController = {
48252 this.templateUrl = 'templates/directory.html';
48254 Directory.Factory = function () {
48255 var directive = function () {
48256 return new Directory();
48262 directives.Directory = Directory;
48263 var DirectoryController = (function () {
48264 function DirectoryController(APIEndPoint, $scope) {
48265 this.APIEndPoint = APIEndPoint;
48266 this.$scope = $scope;
48267 var controller = this;
48269 .getFiles(this.info.fileId)
48271 .then(function (result) {
48272 if (result.status === 'success') {
48273 controller.files = result.info;
48274 angular.forEach(result.info, function (file) {
48275 if (file.fileType === '0') {
48277 if (controller.info.path === '/') {
48278 o.path = '/' + file.name;
48281 o.path = controller.info.path + '/' + file.name;
48283 controller.add()(o, controller.list);
48290 DirectoryController.$inject = ['APIEndPoint', '$scope'];
48291 return DirectoryController;
48293 directives.DirectoryController = DirectoryController;
48294 })(directives = app.directives || (app.directives = {}));
48295 })(app || (app = {}));
48300 (function (directives) {
48301 var Upload = (function () {
48302 function Upload() {
48303 this.restrict = 'E';
48304 this.replace = true;
48306 this.controller = 'UploadController';
48307 this.controllerAs = 'ctrl';
48308 this.bindToController = {
48314 this.templateUrl = 'templates/upload.html';
48315 console.log("templates/upload.html-constructor");
48317 Upload.Factory = function () {
48318 var directive = function () {
48319 return new Upload();
48321 directive.$inject = [];
48326 directives.Upload = Upload;
48327 var UploadController = (function () {
48328 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
48329 this.APIEndPoint = APIEndPoint;
48330 this.$scope = $scope;
48331 this.MyModal = MyModal;
48332 this.WebSocket = WebSocket;
48333 this.$window = $window;
48334 this.$rootScope = $rootScope;
48335 this.Console = Console;
48336 var controller = this;
48337 console.log("directive.upload-constructor");
48339 UploadController.prototype.submit = function () {
48340 console.log("submit: function not supported¥n");
48342 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
48343 return UploadController;
48345 directives.UploadController = UploadController;
48346 })(directives = app.directives || (app.directives = {}));
48347 })(app || (app = {}));
48351 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48353 (function (controllers) {
48354 var Execution = (function () {
48355 function Execution(MyModal, $scope) {
48356 this.MyModal = MyModal;
48357 this.$scope = $scope;
48358 this.commandInfoList = [];
48361 Execution.prototype.add = function () {
48362 this.$scope.$broadcast('close');
48363 var commandInfoList = this.commandInfoList;
48364 var commandInstance = this.MyModal.selectCommand();
48367 .then(function (command) {
48368 commandInfoList.push(new app.declares.CommandInfo(command));
48371 Execution.prototype.open = function () {
48372 var result = this.MyModal.open('SelectCommand');
48373 console.log(result);
48375 Execution.prototype.remove = function (index, list) {
48376 list.splice(index, 1);
48378 Execution.prototype.close = function () {
48379 console.log("close");
48381 Execution.$inject = ['MyModal', '$scope'];
48384 controllers.Execution = Execution;
48385 })(controllers = app.controllers || (app.controllers = {}));
48386 })(app || (app = {}));
48390 (function (controllers) {
48391 var Workspace = (function () {
48392 function Workspace($scope, APIEndPoint, MyModal) {
48393 this.$scope = $scope;
48394 this.APIEndPoint = APIEndPoint;
48395 this.MyModal = MyModal;
48396 this.directoryList = [];
48397 var controller = this;
48398 var directoryList = this.directoryList;
48400 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
48408 directoryList.push(o);
48410 Workspace.prototype.addDirectory = function (info, directoryList) {
48411 directoryList.push(info);
48414 Workspace.prototype.upload = function () {
48415 this.MyModal.upload();
48418 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48419 Workspace.prototype.debug = function () {
48420 this.MyModal.preview();
48422 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
48425 controllers.Workspace = Workspace;
48426 })(controllers = app.controllers || (app.controllers = {}));
48427 })(app || (app = {}));
48431 (function (controllers) {
48432 var History = (function () {
48433 function History($scope) {
48434 this.page = "History";
48436 History.$inject = ['$scope'];
48439 controllers.History = History;
48440 })(controllers = app.controllers || (app.controllers = {}));
48441 })(app || (app = {}));
48445 (function (controllers) {
48446 var SelectCommand = (function () {
48447 function SelectCommand($scope, APIEndPoint, $modalInstance) {
48448 this.APIEndPoint = APIEndPoint;
48449 this.$modalInstance = $modalInstance;
48450 var controller = this;
48453 .$promise.then(function (result) {
48454 controller.tags = result.info;
48458 .$promise.then(function (result) {
48459 controller.commands = result.info;
48461 this.currentTag = 'all';
48463 SelectCommand.prototype.changeTag = function (tag) {
48464 this.currentTag = tag;
48466 SelectCommand.prototype.selectCommand = function (command) {
48467 this.$modalInstance.close(command);
48469 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
48470 return SelectCommand;
48472 controllers.SelectCommand = SelectCommand;
48473 })(controllers = app.controllers || (app.controllers = {}));
48474 })(app || (app = {}));
48478 (function (controllers) {
48480 var Upload = (function () {
48481 function Upload($scope, APIEndPoint, $modalInstance) {
48482 this.APIEndPoint = APIEndPoint;
48483 this.$modalInstance = $modalInstance;
48484 var controller = this;
48485 console.log('controller.upload-controllers');
48487 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
48490 controllers.Upload = Upload;
48491 })(controllers = app.controllers || (app.controllers = {}));
48492 })(app || (app = {}));
48496 (function (controllers) {
48498 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48499 var Preview = (function () {
48500 function Preview($scope, APIEndPoint, $modalInstance) {
48501 this.APIEndPoint = APIEndPoint;
48502 this.$modalInstance = $modalInstance;
48503 var controller = this;
48504 console.log('preview');
48506 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
48509 controllers.Preview = Preview;
48510 })(controllers = app.controllers || (app.controllers = {}));
48511 })(app || (app = {}));
48513 (function (filters) {
48515 return function (commands, tag) {
48517 angular.forEach(commands, function (command) {
48519 angular.forEach(command.tags, function (value) {
48524 result.push(command);
48530 })(filters || (filters = {}));
48534 var appName = 'zephyr';
48535 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
48536 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
48537 $urlRouterProvider.otherwise('/execution');
48538 $locationProvider.html5Mode({
48543 .state('execution', {
48545 templateUrl: 'templates/execution.html',
48546 controller: 'executionController',
48549 .state('workspace', {
48551 templateUrl: 'templates/workspace.html',
48552 controller: 'workspaceController',
48555 .state('history', {
48557 templateUrl: 'templates/history.html',
48558 controller: 'historyController',
48562 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
48563 app.zephyr.service('MyModal', app.services.MyModal);
48564 app.zephyr.service('WebSocket', app.services.WebSocket);
48565 app.zephyr.service('Console', app.services.Console);
48566 app.zephyr.filter('Tag', filters.Tag);
48567 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
48568 app.zephyr.controller('previewController', app.controllers.Preview);
48570 app.zephyr.controller('uploadController', app.controllers.Upload);
48572 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48573 app.zephyr.controller('executionController', app.controllers.Execution);
48574 app.zephyr.controller('workspaceController', app.controllers.Workspace);
48575 app.zephyr.controller('historyController', app.controllers.History);
48576 app.zephyr.controller('commandController', app.directives.CommandController);
48577 app.zephyr.controller('optionController', app.directives.OptionController);
48578 app.zephyr.controller('directoryController', app.directives.DirectoryController);
48579 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
48581 app.zephyr.controller('uploadController', app.directives.UploadController);
48583 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48584 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
48585 app.zephyr.directive('command', app.directives.Command.Factory());
48586 app.zephyr.directive('option', app.directives.Option.Factory());
48587 app.zephyr.directive('directory', app.directives.Directory.Factory());
48588 })(app || (app = {}));
48596 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48597 /***/ function(module, exports) {
48602 (function (declares) {
48603 var CommandInfo = (function () {
48604 function CommandInfo(name) {
48607 return CommandInfo;
48609 declares.CommandInfo = CommandInfo;
48610 })(declares = app.declares || (app.declares = {}));
48611 })(app || (app = {}));
48615 (function (services) {
48616 var APIEndPoint = (function () {
48617 function APIEndPoint($resource, $http) {
48618 this.$resource = $resource;
48619 this.$http = $http;
48621 APIEndPoint.prototype.resource = function (endPoint, data) {
48622 var customAction = {
48628 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
48630 return this.$resource(endPoint, {}, { execute: execute });
48632 APIEndPoint.prototype.getOptionControlFile = function (command) {
48633 var endPoint = '/api/v1/optionControlFile/' + command;
48634 return this.resource(endPoint, {}).get();
48636 APIEndPoint.prototype.getFiles = function (fileId) {
48637 var endPoint = '/api/v1/workspace';
48639 endPoint += '/' + fileId;
48641 return this.resource(endPoint, {}).get();
48643 APIEndPoint.prototype.getDirectories = function () {
48644 var endPoint = '/api/v1/all/workspace/directory';
48645 return this.resource(endPoint, {}).get();
48647 APIEndPoint.prototype.getTags = function () {
48648 var endPoint = '/api/v1/tagList';
48649 return this.resource(endPoint, {}).get();
48651 APIEndPoint.prototype.getCommands = function () {
48652 var endPoint = '/api/v1/commandList';
48653 return this.resource(endPoint, {}).get();
48655 APIEndPoint.prototype.execute = function (data) {
48656 var endPoint = '/api/v1/execution';
48657 var fd = new FormData();
48658 fd.append('data', data);
48659 return this.$http.post(endPoint, fd, {
48660 headers: { 'Content-Type': undefined },
48661 transformRequest: angular.identity
48664 APIEndPoint.prototype.debug = function () {
48665 var endPoint = '/api/v1/debug';
48666 return this.$http.get(endPoint);
48669 APIEndPoint.prototype.upload = function () {
48670 var endPoint = '/api/v1/upload';
48671 return this.$http.get(endPoint);
48674 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48675 APIEndPoint.prototype.help = function (command) {
48676 var endPoint = '/api/v1/help/' + command;
48677 return this.$http.get(endPoint);
48679 return APIEndPoint;
48681 services.APIEndPoint = APIEndPoint;
48682 })(services = app.services || (app.services = {}));
48683 })(app || (app = {}));
48687 (function (services) {
48688 var MyModal = (function () {
48689 function MyModal($uibModal) {
48690 this.$uibModal = $uibModal;
48691 this.modalOption = {
48698 MyModal.prototype.open = function (modalName) {
48699 if (modalName === 'SelectCommand') {
48700 this.modalOption.templateUrl = 'templates/select-command.html';
48701 this.modalOption.size = 'lg';
48703 return this.$uibModal.open(this.modalOption);
48705 MyModal.prototype.selectCommand = function () {
48706 this.modalOption.templateUrl = 'templates/select-command.html';
48707 this.modalOption.controller = 'selectCommandController';
48708 this.modalOption.controllerAs = 'c';
48709 this.modalOption.size = 'lg';
48710 return this.$uibModal.open(this.modalOption);
48712 MyModal.prototype.preview = function () {
48713 this.modalOption.templateUrl = 'templates/preview.html';
48714 this.modalOption.controller = 'previewController';
48715 this.modalOption.controllerAs = 'c';
48716 this.modalOption.size = 'lg';
48717 return this.$uibModal.open(this.modalOption);
48720 MyModal.prototype.upload = function () {
48721 this.modalOption.templateUrl = 'templates/upload.html';
48722 this.modalOption.controller = 'uploadController';
48723 this.modalOption.controllerAs = 'c';
48724 this.modalOption.size = 'lg';
48725 return this.$uibModal.open(this.modalOption);
48728 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
48729 MyModal.$inject = ['$uibModal'];
48732 services.MyModal = MyModal;
48733 })(services = app.services || (app.services = {}));
48734 })(app || (app = {}));
48738 (function (services) {
48739 var WebSocket = (function () {
48740 function WebSocket($rootScope) {
48741 this.$rootScope = $rootScope;
48742 this.socket = io.connect();
48744 WebSocket.prototype.on = function (eventName, callback) {
48745 var socket = this.socket;
48746 var rootScope = this.$rootScope;
48747 socket.on(eventName, function () {
48748 var args = arguments;
48749 rootScope.$apply(function () {
48750 callback.apply(socket, args);
48754 WebSocket.prototype.emit = function (eventName, data, callback) {
48755 var socket = this.socket;
48756 var rootScope = this.$rootScope;
48757 this.socket.emit(eventName, data, function () {
48758 var args = arguments;
48759 rootScope.$apply(function () {
48761 callback.apply(socket, args);
48767 services.WebSocket = WebSocket;
48768 })(services = app.services || (app.services = {}));
48769 })(app || (app = {}));
48773 (function (services) {
48774 var Console = (function () {
48775 function Console(WebSocket, $rootScope) {
48776 this.WebSocket = WebSocket;
48777 this.$rootScope = $rootScope;
48778 this.WebSocket = WebSocket;
48779 this.$rootScope = $rootScope;
48780 this.directiveIDs = [];
48781 var directiveIDs = this.directiveIDs;
48782 this.WebSocket.on('console', function (d) {
48784 var message = d.message;
48785 if (directiveIDs.indexOf(id) > -1) {
48786 $rootScope.$emit(id, message);
48790 Console.prototype.addDirective = function (id) {
48791 if (!(this.directiveIDs.indexOf(id) > -1)) {
48792 this.directiveIDs.push(id);
48795 Console.prototype.removeDirective = function (id) {
48796 var i = this.directiveIDs.indexOf(id);
48798 this.directiveIDs.splice(i, 1);
48801 Console.prototype.showIDs = function () {
48802 console.log(this.directiveIDs);
48806 services.Console = Console;
48807 })(services = app.services || (app.services = {}));
48808 })(app || (app = {}));
48812 (function (directives) {
48813 var Command = (function () {
48814 function Command() {
48815 this.restrict = 'E';
48816 this.replace = true;
48818 this.controller = 'commandController';
48819 this.controllerAs = 'ctrl';
48820 this.bindToController = {
48826 this.templateUrl = 'templates/command.html';
48828 Command.Factory = function () {
48829 var directive = function () {
48830 return new Command();
48832 directive.$inject = [];
48837 directives.Command = Command;
48838 var CommandController = (function () {
48839 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
48840 this.APIEndPoint = APIEndPoint;
48841 this.$scope = $scope;
48842 this.MyModal = MyModal;
48843 this.WebSocket = WebSocket;
48844 this.$window = $window;
48845 this.$rootScope = $rootScope;
48846 this.Console = Console;
48847 var controller = this;
48849 .getOptionControlFile(this.name)
48851 .then(function (result) {
48852 controller.options = result.info;
48857 .then(function (result) {
48858 controller.dirs = result.info;
48860 this.heading = "[" + this.index + "]: dcdFilePrint";
48861 this.isOpen = true;
48862 this.$scope.$on('close', function () {
48863 controller.isOpen = false;
48867 return Math.floor((1 + Math.random()) * 0x10000)
48871 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
48872 s4() + '-' + s4() + s4() + s4();
48874 this.uuid = guid();
48875 this.Console.addDirective(this.uuid);
48876 this.Console.showIDs();
48878 CommandController.prototype.submit = function () {
48880 angular.forEach(this.options, function (option) {
48882 name: option.option,
48885 angular.forEach(option.arg, function (arg) {
48887 if (typeof arg.input === 'object') {
48888 obj.arguments.push(arg.input.name);
48891 obj.arguments.push(arg.input);
48895 if (obj.arguments.length > 0) {
48900 command: this.name,
48901 workspace: this.workspace.fileId,
48905 .execute(JSON.stringify(execObj))
48906 .then(function (result) {
48907 console.log(result);
48910 CommandController.prototype.removeMySelf = function (index) {
48911 this.$scope.$destroy();
48912 this.Console.removeDirective(this.uuid);
48913 this.remove()(index, this.list);
48914 this.Console.showIDs();
48916 CommandController.prototype.reloadFiles = function () {
48918 var fileId = this.workspace.fileId;
48922 .then(function (result) {
48923 var status = result.status;
48924 if (status === 'success') {
48925 _this.files = result.info;
48928 console.log(result.message);
48932 CommandController.prototype.debug = function () {
48933 var div = angular.element(this.$window.document).find("div");
48936 angular.forEach(div, function (v) {
48937 if (v.className === "panel-body console") {
48940 else if (v.className === "row parameters-console") {
48944 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
48945 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
48946 consoleTag.style.height = consoleHeight;
48947 consoleTag.style.width = consoleWidth;
48949 CommandController.prototype.help = function () {
48952 .then(function (result) {
48953 console.log(result);
48956 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
48957 return CommandController;
48959 directives.CommandController = CommandController;
48960 })(directives = app.directives || (app.directives = {}));
48961 })(app || (app = {}));
48965 (function (directives) {
48966 var HeaderMenu = (function () {
48967 function HeaderMenu() {
48968 this.restrict = 'E';
48969 this.replace = true;
48970 this.templateUrl = 'templates/header-menu.html';
48971 this.controller = 'HeaderMenuController';
48972 this.controllerAs = 'hmc';
48975 HeaderMenu.Factory = function () {
48976 var directive = function () {
48977 return new HeaderMenu();
48983 directives.HeaderMenu = HeaderMenu;
48984 var HeaderMenuController = (function () {
48985 function HeaderMenuController($state) {
48986 this.$state = $state;
48987 this.isExecution = this.$state.current.name === 'execution';
48988 this.isWorkspace = this.$state.current.name === 'workspace';
48989 this.isHistory = this.$state.current.name === 'history';
48991 HeaderMenuController.prototype.transit = function (state) {
48992 this.$state.go(state);
48994 HeaderMenuController.$inject = ['$state'];
48995 return HeaderMenuController;
48997 directives.HeaderMenuController = HeaderMenuController;
48998 })(directives = app.directives || (app.directives = {}));
48999 })(app || (app = {}));
49003 (function (directives) {
49004 var Option = (function () {
49005 function Option() {
49006 this.restrict = 'E';
49007 this.replace = true;
49008 this.controller = 'optionController';
49009 this.bindToController = {
49014 this.templateUrl = 'templates/option.html';
49015 this.controllerAs = 'ctrl';
49017 Option.Factory = function () {
49018 var directive = function () {
49019 return new Option();
49021 directive.$inject = [];
49026 directives.Option = Option;
49027 var OptionController = (function () {
49028 function OptionController() {
49029 var controller = this;
49030 angular.forEach(controller.info.arg, function (arg) {
49031 if (arg.initialValue) {
49032 if (arg.formType === 'number') {
49033 arg.input = parseInt(arg.initialValue);
49036 arg.input = arg.initialValue;
49041 OptionController.$inject = [];
49042 return OptionController;
49044 directives.OptionController = OptionController;
49045 })(directives = app.directives || (app.directives = {}));
49046 })(app || (app = {}));
49050 (function (directives) {
49051 var Directory = (function () {
49052 function Directory() {
49053 this.restrict = 'E';
49054 this.replace = true;
49055 this.controller = 'directoryController';
49056 this.controllerAs = 'ctrl';
49057 this.bindToController = {
49063 this.templateUrl = 'templates/directory.html';
49065 Directory.Factory = function () {
49066 var directive = function () {
49067 return new Directory();
49073 directives.Directory = Directory;
49074 var DirectoryController = (function () {
49075 function DirectoryController(APIEndPoint, $scope) {
49076 this.APIEndPoint = APIEndPoint;
49077 this.$scope = $scope;
49078 var controller = this;
49080 .getFiles(this.info.fileId)
49082 .then(function (result) {
49083 if (result.status === 'success') {
49084 controller.files = result.info;
49085 angular.forEach(result.info, function (file) {
49086 if (file.fileType === '0') {
49088 if (controller.info.path === '/') {
49089 o.path = '/' + file.name;
49092 o.path = controller.info.path + '/' + file.name;
49094 controller.add()(o, controller.list);
49101 DirectoryController.$inject = ['APIEndPoint', '$scope'];
49102 return DirectoryController;
49104 directives.DirectoryController = DirectoryController;
49105 })(directives = app.directives || (app.directives = {}));
49106 })(app || (app = {}));
49111 (function (directives) {
49112 var Upload = (function () {
49113 function Upload() {
49114 this.restrict = 'E';
49115 this.replace = true;
49117 this.controller = 'UploadController';
49118 this.controllerAs = 'ctrl';
49119 this.bindToController = {
49125 this.templateUrl = 'templates/upload.html';
49126 console.log("templates/upload.html-constructor");
49128 Upload.Factory = function () {
49129 var directive = function () {
49130 return new Upload();
49132 directive.$inject = [];
49137 directives.Upload = Upload;
49138 var UploadController = (function () {
49139 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
49140 this.APIEndPoint = APIEndPoint;
49141 this.$scope = $scope;
49142 this.MyModal = MyModal;
49143 this.WebSocket = WebSocket;
49144 this.$window = $window;
49145 this.$rootScope = $rootScope;
49146 this.Console = Console;
49147 var controller = this;
49148 console.log("directive.upload-constructor");
49150 UploadController.prototype.submit = function () {
49151 console.log("submit: function not supported¥n");
49153 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
49154 return UploadController;
49156 directives.UploadController = UploadController;
49157 })(directives = app.directives || (app.directives = {}));
49158 })(app || (app = {}));
49162 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49164 (function (controllers) {
49165 var Execution = (function () {
49166 function Execution(MyModal, $scope) {
49167 this.MyModal = MyModal;
49168 this.$scope = $scope;
49169 this.commandInfoList = [];
49172 Execution.prototype.add = function () {
49173 this.$scope.$broadcast('close');
49174 var commandInfoList = this.commandInfoList;
49175 var commandInstance = this.MyModal.selectCommand();
49178 .then(function (command) {
49179 commandInfoList.push(new app.declares.CommandInfo(command));
49182 Execution.prototype.open = function () {
49183 var result = this.MyModal.open('SelectCommand');
49184 console.log(result);
49186 Execution.prototype.remove = function (index, list) {
49187 list.splice(index, 1);
49189 Execution.prototype.close = function () {
49190 console.log("close");
49192 Execution.$inject = ['MyModal', '$scope'];
49195 controllers.Execution = Execution;
49196 })(controllers = app.controllers || (app.controllers = {}));
49197 })(app || (app = {}));
49201 (function (controllers) {
49202 var Workspace = (function () {
49203 function Workspace($scope, APIEndPoint, MyModal) {
49204 this.$scope = $scope;
49205 this.APIEndPoint = APIEndPoint;
49206 this.MyModal = MyModal;
49207 this.directoryList = [];
49208 var controller = this;
49209 var directoryList = this.directoryList;
49211 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
49219 directoryList.push(o);
49221 Workspace.prototype.addDirectory = function (info, directoryList) {
49222 directoryList.push(info);
49225 Workspace.prototype.upload = function () {
49226 this.MyModal.upload();
49229 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49230 Workspace.prototype.debug = function () {
49231 this.MyModal.preview();
49233 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
49236 controllers.Workspace = Workspace;
49237 })(controllers = app.controllers || (app.controllers = {}));
49238 })(app || (app = {}));
49242 (function (controllers) {
49243 var History = (function () {
49244 function History($scope) {
49245 this.page = "History";
49247 History.$inject = ['$scope'];
49250 controllers.History = History;
49251 })(controllers = app.controllers || (app.controllers = {}));
49252 })(app || (app = {}));
49256 (function (controllers) {
49257 var SelectCommand = (function () {
49258 function SelectCommand($scope, APIEndPoint, $modalInstance) {
49259 this.APIEndPoint = APIEndPoint;
49260 this.$modalInstance = $modalInstance;
49261 var controller = this;
49264 .$promise.then(function (result) {
49265 controller.tags = result.info;
49269 .$promise.then(function (result) {
49270 controller.commands = result.info;
49272 this.currentTag = 'all';
49274 SelectCommand.prototype.changeTag = function (tag) {
49275 this.currentTag = tag;
49277 SelectCommand.prototype.selectCommand = function (command) {
49278 this.$modalInstance.close(command);
49280 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
49281 return SelectCommand;
49283 controllers.SelectCommand = SelectCommand;
49284 })(controllers = app.controllers || (app.controllers = {}));
49285 })(app || (app = {}));
49289 (function (controllers) {
49291 var Upload = (function () {
49292 function Upload($scope, APIEndPoint, $modalInstance) {
49293 this.APIEndPoint = APIEndPoint;
49294 this.$modalInstance = $modalInstance;
49295 var controller = this;
49296 console.log('controller.upload-controllers');
49298 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
49301 controllers.Upload = Upload;
49302 })(controllers = app.controllers || (app.controllers = {}));
49303 })(app || (app = {}));
49307 (function (controllers) {
49309 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49310 var Preview = (function () {
49311 function Preview($scope, APIEndPoint, $modalInstance) {
49312 this.APIEndPoint = APIEndPoint;
49313 this.$modalInstance = $modalInstance;
49314 var controller = this;
49315 console.log('preview');
49317 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
49320 controllers.Preview = Preview;
49321 })(controllers = app.controllers || (app.controllers = {}));
49322 })(app || (app = {}));
49324 (function (filters) {
49326 return function (commands, tag) {
49328 angular.forEach(commands, function (command) {
49330 angular.forEach(command.tags, function (value) {
49335 result.push(command);
49341 })(filters || (filters = {}));
49345 var appName = 'zephyr';
49346 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
49347 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
49348 $urlRouterProvider.otherwise('/execution');
49349 $locationProvider.html5Mode({
49354 .state('execution', {
49356 templateUrl: 'templates/execution.html',
49357 controller: 'executionController',
49360 .state('workspace', {
49362 templateUrl: 'templates/workspace.html',
49363 controller: 'workspaceController',
49366 .state('history', {
49368 templateUrl: 'templates/history.html',
49369 controller: 'historyController',
49373 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
49374 app.zephyr.service('MyModal', app.services.MyModal);
49375 app.zephyr.service('WebSocket', app.services.WebSocket);
49376 app.zephyr.service('Console', app.services.Console);
49377 app.zephyr.filter('Tag', filters.Tag);
49378 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
49379 app.zephyr.controller('previewController', app.controllers.Preview);
49381 app.zephyr.controller('uploadController', app.controllers.Upload);
49383 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49384 app.zephyr.controller('executionController', app.controllers.Execution);
49385 app.zephyr.controller('workspaceController', app.controllers.Workspace);
49386 app.zephyr.controller('historyController', app.controllers.History);
49387 app.zephyr.controller('commandController', app.directives.CommandController);
49388 app.zephyr.controller('optionController', app.directives.OptionController);
49389 app.zephyr.controller('directoryController', app.directives.DirectoryController);
49390 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
49392 app.zephyr.controller('uploadController', app.directives.UploadController);
49394 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49395 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
49396 app.zephyr.directive('command', app.directives.Command.Factory());
49397 app.zephyr.directive('option', app.directives.Option.Factory());
49398 app.zephyr.directive('directory', app.directives.Directory.Factory());
49399 })(app || (app = {}));
49407 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49408 /***/ function(module, exports) {
49413 (function (declares) {
49414 var CommandInfo = (function () {
49415 function CommandInfo(name) {
49418 return CommandInfo;
49420 declares.CommandInfo = CommandInfo;
49421 })(declares = app.declares || (app.declares = {}));
49422 })(app || (app = {}));
49426 (function (services) {
49427 var APIEndPoint = (function () {
49428 function APIEndPoint($resource, $http) {
49429 this.$resource = $resource;
49430 this.$http = $http;
49432 APIEndPoint.prototype.resource = function (endPoint, data) {
49433 var customAction = {
49439 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
49441 return this.$resource(endPoint, {}, { execute: execute });
49443 APIEndPoint.prototype.getOptionControlFile = function (command) {
49444 var endPoint = '/api/v1/optionControlFile/' + command;
49445 return this.resource(endPoint, {}).get();
49447 APIEndPoint.prototype.getFiles = function (fileId) {
49448 var endPoint = '/api/v1/workspace';
49450 endPoint += '/' + fileId;
49452 return this.resource(endPoint, {}).get();
49454 APIEndPoint.prototype.getDirectories = function () {
49455 var endPoint = '/api/v1/all/workspace/directory';
49456 return this.resource(endPoint, {}).get();
49458 APIEndPoint.prototype.getTags = function () {
49459 var endPoint = '/api/v1/tagList';
49460 return this.resource(endPoint, {}).get();
49462 APIEndPoint.prototype.getCommands = function () {
49463 var endPoint = '/api/v1/commandList';
49464 return this.resource(endPoint, {}).get();
49466 APIEndPoint.prototype.execute = function (data) {
49467 var endPoint = '/api/v1/execution';
49468 var fd = new FormData();
49469 fd.append('data', data);
49470 return this.$http.post(endPoint, fd, {
49471 headers: { 'Content-Type': undefined },
49472 transformRequest: angular.identity
49475 APIEndPoint.prototype.debug = function () {
49476 var endPoint = '/api/v1/debug';
49477 return this.$http.get(endPoint);
49480 APIEndPoint.prototype.upload = function () {
49481 var endPoint = '/api/v1/upload';
49482 return this.$http.get(endPoint);
49485 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49486 APIEndPoint.prototype.help = function (command) {
49487 var endPoint = '/api/v1/help/' + command;
49488 return this.$http.get(endPoint);
49490 return APIEndPoint;
49492 services.APIEndPoint = APIEndPoint;
49493 })(services = app.services || (app.services = {}));
49494 })(app || (app = {}));
49498 (function (services) {
49499 var MyModal = (function () {
49500 function MyModal($uibModal) {
49501 this.$uibModal = $uibModal;
49502 this.modalOption = {
49509 MyModal.prototype.open = function (modalName) {
49510 if (modalName === 'SelectCommand') {
49511 this.modalOption.templateUrl = 'templates/select-command.html';
49512 this.modalOption.size = 'lg';
49514 return this.$uibModal.open(this.modalOption);
49516 MyModal.prototype.selectCommand = function () {
49517 this.modalOption.templateUrl = 'templates/select-command.html';
49518 this.modalOption.controller = 'selectCommandController';
49519 this.modalOption.controllerAs = 'c';
49520 this.modalOption.size = 'lg';
49521 return this.$uibModal.open(this.modalOption);
49523 MyModal.prototype.preview = function () {
49524 this.modalOption.templateUrl = 'templates/preview.html';
49525 this.modalOption.controller = 'previewController';
49526 this.modalOption.controllerAs = 'c';
49527 this.modalOption.size = 'lg';
49528 return this.$uibModal.open(this.modalOption);
49531 MyModal.prototype.upload = function () {
49532 this.modalOption.templateUrl = 'templates/upload.html';
49533 this.modalOption.controller = 'uploadController';
49534 this.modalOption.controllerAs = 'c';
49535 this.modalOption.size = 'lg';
49536 return this.$uibModal.open(this.modalOption);
49539 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49540 MyModal.$inject = ['$uibModal'];
49543 services.MyModal = MyModal;
49544 })(services = app.services || (app.services = {}));
49545 })(app || (app = {}));
49549 (function (services) {
49550 var WebSocket = (function () {
49551 function WebSocket($rootScope) {
49552 this.$rootScope = $rootScope;
49553 this.socket = io.connect();
49555 WebSocket.prototype.on = function (eventName, callback) {
49556 var socket = this.socket;
49557 var rootScope = this.$rootScope;
49558 socket.on(eventName, function () {
49559 var args = arguments;
49560 rootScope.$apply(function () {
49561 callback.apply(socket, args);
49565 WebSocket.prototype.emit = function (eventName, data, callback) {
49566 var socket = this.socket;
49567 var rootScope = this.$rootScope;
49568 this.socket.emit(eventName, data, function () {
49569 var args = arguments;
49570 rootScope.$apply(function () {
49572 callback.apply(socket, args);
49578 services.WebSocket = WebSocket;
49579 })(services = app.services || (app.services = {}));
49580 })(app || (app = {}));
49584 (function (services) {
49585 var Console = (function () {
49586 function Console(WebSocket, $rootScope) {
49587 this.WebSocket = WebSocket;
49588 this.$rootScope = $rootScope;
49589 this.WebSocket = WebSocket;
49590 this.$rootScope = $rootScope;
49591 this.directiveIDs = [];
49592 var directiveIDs = this.directiveIDs;
49593 this.WebSocket.on('console', function (d) {
49595 var message = d.message;
49596 if (directiveIDs.indexOf(id) > -1) {
49597 $rootScope.$emit(id, message);
49601 Console.prototype.addDirective = function (id) {
49602 if (!(this.directiveIDs.indexOf(id) > -1)) {
49603 this.directiveIDs.push(id);
49606 Console.prototype.removeDirective = function (id) {
49607 var i = this.directiveIDs.indexOf(id);
49609 this.directiveIDs.splice(i, 1);
49612 Console.prototype.showIDs = function () {
49613 console.log(this.directiveIDs);
49617 services.Console = Console;
49618 })(services = app.services || (app.services = {}));
49619 })(app || (app = {}));
49623 (function (directives) {
49624 var Command = (function () {
49625 function Command() {
49626 this.restrict = 'E';
49627 this.replace = true;
49629 this.controller = 'commandController';
49630 this.controllerAs = 'ctrl';
49631 this.bindToController = {
49637 this.templateUrl = 'templates/command.html';
49639 Command.Factory = function () {
49640 var directive = function () {
49641 return new Command();
49643 directive.$inject = [];
49648 directives.Command = Command;
49649 var CommandController = (function () {
49650 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
49651 this.APIEndPoint = APIEndPoint;
49652 this.$scope = $scope;
49653 this.MyModal = MyModal;
49654 this.WebSocket = WebSocket;
49655 this.$window = $window;
49656 this.$rootScope = $rootScope;
49657 this.Console = Console;
49658 var controller = this;
49660 .getOptionControlFile(this.name)
49662 .then(function (result) {
49663 controller.options = result.info;
49668 .then(function (result) {
49669 controller.dirs = result.info;
49671 this.heading = "[" + this.index + "]: dcdFilePrint";
49672 this.isOpen = true;
49673 this.$scope.$on('close', function () {
49674 controller.isOpen = false;
49678 return Math.floor((1 + Math.random()) * 0x10000)
49682 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
49683 s4() + '-' + s4() + s4() + s4();
49685 this.uuid = guid();
49686 this.Console.addDirective(this.uuid);
49687 this.Console.showIDs();
49689 CommandController.prototype.submit = function () {
49691 angular.forEach(this.options, function (option) {
49693 name: option.option,
49696 angular.forEach(option.arg, function (arg) {
49698 if (typeof arg.input === 'object') {
49699 obj.arguments.push(arg.input.name);
49702 obj.arguments.push(arg.input);
49706 if (obj.arguments.length > 0) {
49711 command: this.name,
49712 workspace: this.workspace.fileId,
49716 .execute(JSON.stringify(execObj))
49717 .then(function (result) {
49718 console.log(result);
49721 CommandController.prototype.removeMySelf = function (index) {
49722 this.$scope.$destroy();
49723 this.Console.removeDirective(this.uuid);
49724 this.remove()(index, this.list);
49725 this.Console.showIDs();
49727 CommandController.prototype.reloadFiles = function () {
49729 var fileId = this.workspace.fileId;
49733 .then(function (result) {
49734 var status = result.status;
49735 if (status === 'success') {
49736 _this.files = result.info;
49739 console.log(result.message);
49743 CommandController.prototype.debug = function () {
49744 var div = angular.element(this.$window.document).find("div");
49747 angular.forEach(div, function (v) {
49748 if (v.className === "panel-body console") {
49751 else if (v.className === "row parameters-console") {
49755 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
49756 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
49757 consoleTag.style.height = consoleHeight;
49758 consoleTag.style.width = consoleWidth;
49760 CommandController.prototype.help = function () {
49763 .then(function (result) {
49764 console.log(result);
49767 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
49768 return CommandController;
49770 directives.CommandController = CommandController;
49771 })(directives = app.directives || (app.directives = {}));
49772 })(app || (app = {}));
49776 (function (directives) {
49777 var HeaderMenu = (function () {
49778 function HeaderMenu() {
49779 this.restrict = 'E';
49780 this.replace = true;
49781 this.templateUrl = 'templates/header-menu.html';
49782 this.controller = 'HeaderMenuController';
49783 this.controllerAs = 'hmc';
49786 HeaderMenu.Factory = function () {
49787 var directive = function () {
49788 return new HeaderMenu();
49794 directives.HeaderMenu = HeaderMenu;
49795 var HeaderMenuController = (function () {
49796 function HeaderMenuController($state) {
49797 this.$state = $state;
49798 this.isExecution = this.$state.current.name === 'execution';
49799 this.isWorkspace = this.$state.current.name === 'workspace';
49800 this.isHistory = this.$state.current.name === 'history';
49802 HeaderMenuController.prototype.transit = function (state) {
49803 this.$state.go(state);
49805 HeaderMenuController.$inject = ['$state'];
49806 return HeaderMenuController;
49808 directives.HeaderMenuController = HeaderMenuController;
49809 })(directives = app.directives || (app.directives = {}));
49810 })(app || (app = {}));
49814 (function (directives) {
49815 var Option = (function () {
49816 function Option() {
49817 this.restrict = 'E';
49818 this.replace = true;
49819 this.controller = 'optionController';
49820 this.bindToController = {
49825 this.templateUrl = 'templates/option.html';
49826 this.controllerAs = 'ctrl';
49828 Option.Factory = function () {
49829 var directive = function () {
49830 return new Option();
49832 directive.$inject = [];
49837 directives.Option = Option;
49838 var OptionController = (function () {
49839 function OptionController() {
49840 var controller = this;
49841 angular.forEach(controller.info.arg, function (arg) {
49842 if (arg.initialValue) {
49843 if (arg.formType === 'number') {
49844 arg.input = parseInt(arg.initialValue);
49847 arg.input = arg.initialValue;
49852 OptionController.$inject = [];
49853 return OptionController;
49855 directives.OptionController = OptionController;
49856 })(directives = app.directives || (app.directives = {}));
49857 })(app || (app = {}));
49861 (function (directives) {
49862 var Directory = (function () {
49863 function Directory() {
49864 this.restrict = 'E';
49865 this.replace = true;
49866 this.controller = 'directoryController';
49867 this.controllerAs = 'ctrl';
49868 this.bindToController = {
49874 this.templateUrl = 'templates/directory.html';
49876 Directory.Factory = function () {
49877 var directive = function () {
49878 return new Directory();
49884 directives.Directory = Directory;
49885 var DirectoryController = (function () {
49886 function DirectoryController(APIEndPoint, $scope) {
49887 this.APIEndPoint = APIEndPoint;
49888 this.$scope = $scope;
49889 var controller = this;
49891 .getFiles(this.info.fileId)
49893 .then(function (result) {
49894 if (result.status === 'success') {
49895 controller.files = result.info;
49896 angular.forEach(result.info, function (file) {
49897 if (file.fileType === '0') {
49899 if (controller.info.path === '/') {
49900 o.path = '/' + file.name;
49903 o.path = controller.info.path + '/' + file.name;
49905 controller.add()(o, controller.list);
49912 DirectoryController.$inject = ['APIEndPoint', '$scope'];
49913 return DirectoryController;
49915 directives.DirectoryController = DirectoryController;
49916 })(directives = app.directives || (app.directives = {}));
49917 })(app || (app = {}));
49922 (function (directives) {
49923 var Upload = (function () {
49924 function Upload() {
49925 this.restrict = 'E';
49926 this.replace = true;
49928 this.controller = 'UploadController';
49929 this.controllerAs = 'ctrl';
49930 this.bindToController = {
49936 this.templateUrl = 'templates/upload.html';
49937 console.log("templates/upload.html-constructor");
49939 Upload.Factory = function () {
49940 var directive = function () {
49941 return new Upload();
49943 directive.$inject = [];
49948 directives.Upload = Upload;
49949 var UploadController = (function () {
49950 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
49951 this.APIEndPoint = APIEndPoint;
49952 this.$scope = $scope;
49953 this.MyModal = MyModal;
49954 this.WebSocket = WebSocket;
49955 this.$window = $window;
49956 this.$rootScope = $rootScope;
49957 this.Console = Console;
49958 var controller = this;
49959 console.log("directive.upload-constructor");
49961 UploadController.prototype.submit = function () {
49962 console.log("submit: function not supported¥n");
49964 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
49965 return UploadController;
49967 directives.UploadController = UploadController;
49968 })(directives = app.directives || (app.directives = {}));
49969 })(app || (app = {}));
49973 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
49975 (function (controllers) {
49976 var Execution = (function () {
49977 function Execution(MyModal, $scope) {
49978 this.MyModal = MyModal;
49979 this.$scope = $scope;
49980 this.commandInfoList = [];
49983 Execution.prototype.add = function () {
49984 this.$scope.$broadcast('close');
49985 var commandInfoList = this.commandInfoList;
49986 var commandInstance = this.MyModal.selectCommand();
49989 .then(function (command) {
49990 commandInfoList.push(new app.declares.CommandInfo(command));
49993 Execution.prototype.open = function () {
49994 var result = this.MyModal.open('SelectCommand');
49995 console.log(result);
49997 Execution.prototype.remove = function (index, list) {
49998 list.splice(index, 1);
50000 Execution.prototype.close = function () {
50001 console.log("close");
50003 Execution.$inject = ['MyModal', '$scope'];
50006 controllers.Execution = Execution;
50007 })(controllers = app.controllers || (app.controllers = {}));
50008 })(app || (app = {}));
50012 (function (controllers) {
50013 var Workspace = (function () {
50014 function Workspace($scope, APIEndPoint, MyModal) {
50015 this.$scope = $scope;
50016 this.APIEndPoint = APIEndPoint;
50017 this.MyModal = MyModal;
50018 this.directoryList = [];
50019 var controller = this;
50020 var directoryList = this.directoryList;
50022 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
50030 directoryList.push(o);
50032 Workspace.prototype.addDirectory = function (info, directoryList) {
50033 directoryList.push(info);
50036 Workspace.prototype.upload = function () {
50037 this.MyModal.upload();
50040 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50041 Workspace.prototype.debug = function () {
50042 this.MyModal.preview();
50044 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
50047 controllers.Workspace = Workspace;
50048 })(controllers = app.controllers || (app.controllers = {}));
50049 })(app || (app = {}));
50053 (function (controllers) {
50054 var History = (function () {
50055 function History($scope) {
50056 this.page = "History";
50058 History.$inject = ['$scope'];
50061 controllers.History = History;
50062 })(controllers = app.controllers || (app.controllers = {}));
50063 })(app || (app = {}));
50067 (function (controllers) {
50068 var SelectCommand = (function () {
50069 function SelectCommand($scope, APIEndPoint, $modalInstance) {
50070 this.APIEndPoint = APIEndPoint;
50071 this.$modalInstance = $modalInstance;
50072 var controller = this;
50075 .$promise.then(function (result) {
50076 controller.tags = result.info;
50080 .$promise.then(function (result) {
50081 controller.commands = result.info;
50083 this.currentTag = 'all';
50085 SelectCommand.prototype.changeTag = function (tag) {
50086 this.currentTag = tag;
50088 SelectCommand.prototype.selectCommand = function (command) {
50089 this.$modalInstance.close(command);
50091 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50092 return SelectCommand;
50094 controllers.SelectCommand = SelectCommand;
50095 })(controllers = app.controllers || (app.controllers = {}));
50096 })(app || (app = {}));
50100 (function (controllers) {
50102 var Upload = (function () {
50103 function Upload($scope, APIEndPoint, $modalInstance) {
50104 this.APIEndPoint = APIEndPoint;
50105 this.$modalInstance = $modalInstance;
50106 var controller = this;
50107 console.log('controller.upload-controllers');
50109 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50112 controllers.Upload = Upload;
50113 })(controllers = app.controllers || (app.controllers = {}));
50114 })(app || (app = {}));
50118 (function (controllers) {
50120 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50121 var Preview = (function () {
50122 function Preview($scope, APIEndPoint, $modalInstance) {
50123 this.APIEndPoint = APIEndPoint;
50124 this.$modalInstance = $modalInstance;
50125 var controller = this;
50126 console.log('preview');
50128 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50131 controllers.Preview = Preview;
50132 })(controllers = app.controllers || (app.controllers = {}));
50133 })(app || (app = {}));
50135 (function (filters) {
50137 return function (commands, tag) {
50139 angular.forEach(commands, function (command) {
50141 angular.forEach(command.tags, function (value) {
50146 result.push(command);
50152 })(filters || (filters = {}));
50156 var appName = 'zephyr';
50157 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
50158 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
50159 $urlRouterProvider.otherwise('/execution');
50160 $locationProvider.html5Mode({
50165 .state('execution', {
50167 templateUrl: 'templates/execution.html',
50168 controller: 'executionController',
50171 .state('workspace', {
50173 templateUrl: 'templates/workspace.html',
50174 controller: 'workspaceController',
50177 .state('history', {
50179 templateUrl: 'templates/history.html',
50180 controller: 'historyController',
50184 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
50185 app.zephyr.service('MyModal', app.services.MyModal);
50186 app.zephyr.service('WebSocket', app.services.WebSocket);
50187 app.zephyr.service('Console', app.services.Console);
50188 app.zephyr.filter('Tag', filters.Tag);
50189 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
50190 app.zephyr.controller('previewController', app.controllers.Preview);
50192 app.zephyr.controller('uploadController', app.controllers.Upload);
50194 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50195 app.zephyr.controller('executionController', app.controllers.Execution);
50196 app.zephyr.controller('workspaceController', app.controllers.Workspace);
50197 app.zephyr.controller('historyController', app.controllers.History);
50198 app.zephyr.controller('commandController', app.directives.CommandController);
50199 app.zephyr.controller('optionController', app.directives.OptionController);
50200 app.zephyr.controller('directoryController', app.directives.DirectoryController);
50201 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
50203 app.zephyr.controller('uploadController', app.directives.UploadController);
50205 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50206 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
50207 app.zephyr.directive('command', app.directives.Command.Factory());
50208 app.zephyr.directive('option', app.directives.Option.Factory());
50209 app.zephyr.directive('directory', app.directives.Directory.Factory());
50210 })(app || (app = {}));
50218 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50219 /***/ function(module, exports) {
50224 (function (declares) {
50225 var CommandInfo = (function () {
50226 function CommandInfo(name) {
50229 return CommandInfo;
50231 declares.CommandInfo = CommandInfo;
50232 })(declares = app.declares || (app.declares = {}));
50233 })(app || (app = {}));
50237 (function (services) {
50238 var APIEndPoint = (function () {
50239 function APIEndPoint($resource, $http) {
50240 this.$resource = $resource;
50241 this.$http = $http;
50243 APIEndPoint.prototype.resource = function (endPoint, data) {
50244 var customAction = {
50250 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
50252 return this.$resource(endPoint, {}, { execute: execute });
50254 APIEndPoint.prototype.getOptionControlFile = function (command) {
50255 var endPoint = '/api/v1/optionControlFile/' + command;
50256 return this.resource(endPoint, {}).get();
50258 APIEndPoint.prototype.getFiles = function (fileId) {
50259 var endPoint = '/api/v1/workspace';
50261 endPoint += '/' + fileId;
50263 return this.resource(endPoint, {}).get();
50265 APIEndPoint.prototype.getDirectories = function () {
50266 var endPoint = '/api/v1/all/workspace/directory';
50267 return this.resource(endPoint, {}).get();
50269 APIEndPoint.prototype.getTags = function () {
50270 var endPoint = '/api/v1/tagList';
50271 return this.resource(endPoint, {}).get();
50273 APIEndPoint.prototype.getCommands = function () {
50274 var endPoint = '/api/v1/commandList';
50275 return this.resource(endPoint, {}).get();
50277 APIEndPoint.prototype.execute = function (data) {
50278 var endPoint = '/api/v1/execution';
50279 var fd = new FormData();
50280 fd.append('data', data);
50281 return this.$http.post(endPoint, fd, {
50282 headers: { 'Content-Type': undefined },
50283 transformRequest: angular.identity
50286 APIEndPoint.prototype.debug = function () {
50287 var endPoint = '/api/v1/debug';
50288 return this.$http.get(endPoint);
50291 APIEndPoint.prototype.upload = function () {
50292 var endPoint = '/api/v1/upload';
50293 return this.$http.get(endPoint);
50296 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50297 APIEndPoint.prototype.help = function (command) {
50298 var endPoint = '/api/v1/help/' + command;
50299 return this.$http.get(endPoint);
50301 return APIEndPoint;
50303 services.APIEndPoint = APIEndPoint;
50304 })(services = app.services || (app.services = {}));
50305 })(app || (app = {}));
50309 (function (services) {
50310 var MyModal = (function () {
50311 function MyModal($uibModal) {
50312 this.$uibModal = $uibModal;
50313 this.modalOption = {
50320 MyModal.prototype.open = function (modalName) {
50321 if (modalName === 'SelectCommand') {
50322 this.modalOption.templateUrl = 'templates/select-command.html';
50323 this.modalOption.size = 'lg';
50325 return this.$uibModal.open(this.modalOption);
50327 MyModal.prototype.selectCommand = function () {
50328 this.modalOption.templateUrl = 'templates/select-command.html';
50329 this.modalOption.controller = 'selectCommandController';
50330 this.modalOption.controllerAs = 'c';
50331 this.modalOption.size = 'lg';
50332 return this.$uibModal.open(this.modalOption);
50334 MyModal.prototype.preview = function () {
50335 this.modalOption.templateUrl = 'templates/preview.html';
50336 this.modalOption.controller = 'previewController';
50337 this.modalOption.controllerAs = 'c';
50338 this.modalOption.size = 'lg';
50339 return this.$uibModal.open(this.modalOption);
50342 MyModal.prototype.upload = function () {
50343 this.modalOption.templateUrl = 'templates/upload.html';
50344 this.modalOption.controller = 'uploadController';
50345 this.modalOption.controllerAs = 'c';
50346 this.modalOption.size = 'lg';
50347 return this.$uibModal.open(this.modalOption);
50350 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50351 MyModal.$inject = ['$uibModal'];
50354 services.MyModal = MyModal;
50355 })(services = app.services || (app.services = {}));
50356 })(app || (app = {}));
50360 (function (services) {
50361 var WebSocket = (function () {
50362 function WebSocket($rootScope) {
50363 this.$rootScope = $rootScope;
50364 this.socket = io.connect();
50366 WebSocket.prototype.on = function (eventName, callback) {
50367 var socket = this.socket;
50368 var rootScope = this.$rootScope;
50369 socket.on(eventName, function () {
50370 var args = arguments;
50371 rootScope.$apply(function () {
50372 callback.apply(socket, args);
50376 WebSocket.prototype.emit = function (eventName, data, callback) {
50377 var socket = this.socket;
50378 var rootScope = this.$rootScope;
50379 this.socket.emit(eventName, data, function () {
50380 var args = arguments;
50381 rootScope.$apply(function () {
50383 callback.apply(socket, args);
50389 services.WebSocket = WebSocket;
50390 })(services = app.services || (app.services = {}));
50391 })(app || (app = {}));
50395 (function (services) {
50396 var Console = (function () {
50397 function Console(WebSocket, $rootScope) {
50398 this.WebSocket = WebSocket;
50399 this.$rootScope = $rootScope;
50400 this.WebSocket = WebSocket;
50401 this.$rootScope = $rootScope;
50402 this.directiveIDs = [];
50403 var directiveIDs = this.directiveIDs;
50404 this.WebSocket.on('console', function (d) {
50406 var message = d.message;
50407 if (directiveIDs.indexOf(id) > -1) {
50408 $rootScope.$emit(id, message);
50412 Console.prototype.addDirective = function (id) {
50413 if (!(this.directiveIDs.indexOf(id) > -1)) {
50414 this.directiveIDs.push(id);
50417 Console.prototype.removeDirective = function (id) {
50418 var i = this.directiveIDs.indexOf(id);
50420 this.directiveIDs.splice(i, 1);
50423 Console.prototype.showIDs = function () {
50424 console.log(this.directiveIDs);
50428 services.Console = Console;
50429 })(services = app.services || (app.services = {}));
50430 })(app || (app = {}));
50434 (function (directives) {
50435 var Command = (function () {
50436 function Command() {
50437 this.restrict = 'E';
50438 this.replace = true;
50440 this.controller = 'commandController';
50441 this.controllerAs = 'ctrl';
50442 this.bindToController = {
50448 this.templateUrl = 'templates/command.html';
50450 Command.Factory = function () {
50451 var directive = function () {
50452 return new Command();
50454 directive.$inject = [];
50459 directives.Command = Command;
50460 var CommandController = (function () {
50461 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
50462 this.APIEndPoint = APIEndPoint;
50463 this.$scope = $scope;
50464 this.MyModal = MyModal;
50465 this.WebSocket = WebSocket;
50466 this.$window = $window;
50467 this.$rootScope = $rootScope;
50468 this.Console = Console;
50469 var controller = this;
50471 .getOptionControlFile(this.name)
50473 .then(function (result) {
50474 controller.options = result.info;
50479 .then(function (result) {
50480 controller.dirs = result.info;
50482 this.heading = "[" + this.index + "]: dcdFilePrint";
50483 this.isOpen = true;
50484 this.$scope.$on('close', function () {
50485 controller.isOpen = false;
50489 return Math.floor((1 + Math.random()) * 0x10000)
50493 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
50494 s4() + '-' + s4() + s4() + s4();
50496 this.uuid = guid();
50497 this.Console.addDirective(this.uuid);
50498 this.Console.showIDs();
50500 CommandController.prototype.submit = function () {
50502 angular.forEach(this.options, function (option) {
50504 name: option.option,
50507 angular.forEach(option.arg, function (arg) {
50509 if (typeof arg.input === 'object') {
50510 obj.arguments.push(arg.input.name);
50513 obj.arguments.push(arg.input);
50517 if (obj.arguments.length > 0) {
50522 command: this.name,
50523 workspace: this.workspace.fileId,
50527 .execute(JSON.stringify(execObj))
50528 .then(function (result) {
50529 console.log(result);
50532 CommandController.prototype.removeMySelf = function (index) {
50533 this.$scope.$destroy();
50534 this.Console.removeDirective(this.uuid);
50535 this.remove()(index, this.list);
50536 this.Console.showIDs();
50538 CommandController.prototype.reloadFiles = function () {
50540 var fileId = this.workspace.fileId;
50544 .then(function (result) {
50545 var status = result.status;
50546 if (status === 'success') {
50547 _this.files = result.info;
50550 console.log(result.message);
50554 CommandController.prototype.debug = function () {
50555 var div = angular.element(this.$window.document).find("div");
50558 angular.forEach(div, function (v) {
50559 if (v.className === "panel-body console") {
50562 else if (v.className === "row parameters-console") {
50566 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
50567 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
50568 consoleTag.style.height = consoleHeight;
50569 consoleTag.style.width = consoleWidth;
50571 CommandController.prototype.help = function () {
50574 .then(function (result) {
50575 console.log(result);
50578 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
50579 return CommandController;
50581 directives.CommandController = CommandController;
50582 })(directives = app.directives || (app.directives = {}));
50583 })(app || (app = {}));
50587 (function (directives) {
50588 var HeaderMenu = (function () {
50589 function HeaderMenu() {
50590 this.restrict = 'E';
50591 this.replace = true;
50592 this.templateUrl = 'templates/header-menu.html';
50593 this.controller = 'HeaderMenuController';
50594 this.controllerAs = 'hmc';
50597 HeaderMenu.Factory = function () {
50598 var directive = function () {
50599 return new HeaderMenu();
50605 directives.HeaderMenu = HeaderMenu;
50606 var HeaderMenuController = (function () {
50607 function HeaderMenuController($state) {
50608 this.$state = $state;
50609 this.isExecution = this.$state.current.name === 'execution';
50610 this.isWorkspace = this.$state.current.name === 'workspace';
50611 this.isHistory = this.$state.current.name === 'history';
50613 HeaderMenuController.prototype.transit = function (state) {
50614 this.$state.go(state);
50616 HeaderMenuController.$inject = ['$state'];
50617 return HeaderMenuController;
50619 directives.HeaderMenuController = HeaderMenuController;
50620 })(directives = app.directives || (app.directives = {}));
50621 })(app || (app = {}));
50625 (function (directives) {
50626 var Option = (function () {
50627 function Option() {
50628 this.restrict = 'E';
50629 this.replace = true;
50630 this.controller = 'optionController';
50631 this.bindToController = {
50636 this.templateUrl = 'templates/option.html';
50637 this.controllerAs = 'ctrl';
50639 Option.Factory = function () {
50640 var directive = function () {
50641 return new Option();
50643 directive.$inject = [];
50648 directives.Option = Option;
50649 var OptionController = (function () {
50650 function OptionController() {
50651 var controller = this;
50652 angular.forEach(controller.info.arg, function (arg) {
50653 if (arg.initialValue) {
50654 if (arg.formType === 'number') {
50655 arg.input = parseInt(arg.initialValue);
50658 arg.input = arg.initialValue;
50663 OptionController.$inject = [];
50664 return OptionController;
50666 directives.OptionController = OptionController;
50667 })(directives = app.directives || (app.directives = {}));
50668 })(app || (app = {}));
50672 (function (directives) {
50673 var Directory = (function () {
50674 function Directory() {
50675 this.restrict = 'E';
50676 this.replace = true;
50677 this.controller = 'directoryController';
50678 this.controllerAs = 'ctrl';
50679 this.bindToController = {
50685 this.templateUrl = 'templates/directory.html';
50687 Directory.Factory = function () {
50688 var directive = function () {
50689 return new Directory();
50695 directives.Directory = Directory;
50696 var DirectoryController = (function () {
50697 function DirectoryController(APIEndPoint, $scope) {
50698 this.APIEndPoint = APIEndPoint;
50699 this.$scope = $scope;
50700 var controller = this;
50702 .getFiles(this.info.fileId)
50704 .then(function (result) {
50705 if (result.status === 'success') {
50706 controller.files = result.info;
50707 angular.forEach(result.info, function (file) {
50708 if (file.fileType === '0') {
50710 if (controller.info.path === '/') {
50711 o.path = '/' + file.name;
50714 o.path = controller.info.path + '/' + file.name;
50716 controller.add()(o, controller.list);
50723 DirectoryController.$inject = ['APIEndPoint', '$scope'];
50724 return DirectoryController;
50726 directives.DirectoryController = DirectoryController;
50727 })(directives = app.directives || (app.directives = {}));
50728 })(app || (app = {}));
50733 (function (directives) {
50734 var Upload = (function () {
50735 function Upload() {
50736 this.restrict = 'E';
50737 this.replace = true;
50739 this.controller = 'UploadController';
50740 this.controllerAs = 'ctrl';
50741 this.bindToController = {
50747 this.templateUrl = 'templates/upload.html';
50748 console.log("templates/upload.html-constructor");
50750 Upload.Factory = function () {
50751 var directive = function () {
50752 return new Upload();
50754 directive.$inject = [];
50759 directives.Upload = Upload;
50760 var UploadController = (function () {
50761 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
50762 this.APIEndPoint = APIEndPoint;
50763 this.$scope = $scope;
50764 this.MyModal = MyModal;
50765 this.WebSocket = WebSocket;
50766 this.$window = $window;
50767 this.$rootScope = $rootScope;
50768 this.Console = Console;
50769 var controller = this;
50770 console.log("directive.upload-constructor");
50772 UploadController.prototype.submit = function () {
50773 console.log("submit: function not supported¥n");
50775 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
50776 return UploadController;
50778 directives.UploadController = UploadController;
50779 })(directives = app.directives || (app.directives = {}));
50780 })(app || (app = {}));
50784 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50786 (function (controllers) {
50787 var Execution = (function () {
50788 function Execution(MyModal, $scope) {
50789 this.MyModal = MyModal;
50790 this.$scope = $scope;
50791 this.commandInfoList = [];
50794 Execution.prototype.add = function () {
50795 this.$scope.$broadcast('close');
50796 var commandInfoList = this.commandInfoList;
50797 var commandInstance = this.MyModal.selectCommand();
50800 .then(function (command) {
50801 commandInfoList.push(new app.declares.CommandInfo(command));
50804 Execution.prototype.open = function () {
50805 var result = this.MyModal.open('SelectCommand');
50806 console.log(result);
50808 Execution.prototype.remove = function (index, list) {
50809 list.splice(index, 1);
50811 Execution.prototype.close = function () {
50812 console.log("close");
50814 Execution.$inject = ['MyModal', '$scope'];
50817 controllers.Execution = Execution;
50818 })(controllers = app.controllers || (app.controllers = {}));
50819 })(app || (app = {}));
50823 (function (controllers) {
50824 var Workspace = (function () {
50825 function Workspace($scope, APIEndPoint, MyModal) {
50826 this.$scope = $scope;
50827 this.APIEndPoint = APIEndPoint;
50828 this.MyModal = MyModal;
50829 this.directoryList = [];
50830 var controller = this;
50831 var directoryList = this.directoryList;
50833 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
50841 directoryList.push(o);
50843 Workspace.prototype.addDirectory = function (info, directoryList) {
50844 directoryList.push(info);
50847 Workspace.prototype.upload = function () {
50848 this.MyModal.upload();
50851 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50852 Workspace.prototype.debug = function () {
50853 this.MyModal.preview();
50855 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
50858 controllers.Workspace = Workspace;
50859 })(controllers = app.controllers || (app.controllers = {}));
50860 })(app || (app = {}));
50864 (function (controllers) {
50865 var History = (function () {
50866 function History($scope) {
50867 this.page = "History";
50869 History.$inject = ['$scope'];
50872 controllers.History = History;
50873 })(controllers = app.controllers || (app.controllers = {}));
50874 })(app || (app = {}));
50878 (function (controllers) {
50879 var SelectCommand = (function () {
50880 function SelectCommand($scope, APIEndPoint, $modalInstance) {
50881 this.APIEndPoint = APIEndPoint;
50882 this.$modalInstance = $modalInstance;
50883 var controller = this;
50886 .$promise.then(function (result) {
50887 controller.tags = result.info;
50891 .$promise.then(function (result) {
50892 controller.commands = result.info;
50894 this.currentTag = 'all';
50896 SelectCommand.prototype.changeTag = function (tag) {
50897 this.currentTag = tag;
50899 SelectCommand.prototype.selectCommand = function (command) {
50900 this.$modalInstance.close(command);
50902 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50903 return SelectCommand;
50905 controllers.SelectCommand = SelectCommand;
50906 })(controllers = app.controllers || (app.controllers = {}));
50907 })(app || (app = {}));
50911 (function (controllers) {
50913 var Upload = (function () {
50914 function Upload($scope, APIEndPoint, $modalInstance) {
50915 this.APIEndPoint = APIEndPoint;
50916 this.$modalInstance = $modalInstance;
50917 var controller = this;
50918 console.log('controller.upload-controllers');
50920 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50923 controllers.Upload = Upload;
50924 })(controllers = app.controllers || (app.controllers = {}));
50925 })(app || (app = {}));
50929 (function (controllers) {
50931 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
50932 var Preview = (function () {
50933 function Preview($scope, APIEndPoint, $modalInstance) {
50934 this.APIEndPoint = APIEndPoint;
50935 this.$modalInstance = $modalInstance;
50936 var controller = this;
50937 console.log('preview');
50939 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
50942 controllers.Preview = Preview;
50943 })(controllers = app.controllers || (app.controllers = {}));
50944 })(app || (app = {}));
50946 (function (filters) {
50948 return function (commands, tag) {
50950 angular.forEach(commands, function (command) {
50952 angular.forEach(command.tags, function (value) {
50957 result.push(command);
50963 })(filters || (filters = {}));
50967 var appName = 'zephyr';
50968 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
50969 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
50970 $urlRouterProvider.otherwise('/execution');
50971 $locationProvider.html5Mode({
50976 .state('execution', {
50978 templateUrl: 'templates/execution.html',
50979 controller: 'executionController',
50982 .state('workspace', {
50984 templateUrl: 'templates/workspace.html',
50985 controller: 'workspaceController',
50988 .state('history', {
50990 templateUrl: 'templates/history.html',
50991 controller: 'historyController',
50995 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
50996 app.zephyr.service('MyModal', app.services.MyModal);
50997 app.zephyr.service('WebSocket', app.services.WebSocket);
50998 app.zephyr.service('Console', app.services.Console);
50999 app.zephyr.filter('Tag', filters.Tag);
51000 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
51001 app.zephyr.controller('previewController', app.controllers.Preview);
51003 app.zephyr.controller('uploadController', app.controllers.Upload);
51005 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51006 app.zephyr.controller('executionController', app.controllers.Execution);
51007 app.zephyr.controller('workspaceController', app.controllers.Workspace);
51008 app.zephyr.controller('historyController', app.controllers.History);
51009 app.zephyr.controller('commandController', app.directives.CommandController);
51010 app.zephyr.controller('optionController', app.directives.OptionController);
51011 app.zephyr.controller('directoryController', app.directives.DirectoryController);
51012 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
51014 app.zephyr.controller('uploadController', app.directives.UploadController);
51016 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51017 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
51018 app.zephyr.directive('command', app.directives.Command.Factory());
51019 app.zephyr.directive('option', app.directives.Option.Factory());
51020 app.zephyr.directive('directory', app.directives.Directory.Factory());
51021 })(app || (app = {}));
51029 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51030 /***/ function(module, exports) {
51035 (function (declares) {
51036 var CommandInfo = (function () {
51037 function CommandInfo(name) {
51040 return CommandInfo;
51042 declares.CommandInfo = CommandInfo;
51043 })(declares = app.declares || (app.declares = {}));
51044 })(app || (app = {}));
51048 (function (services) {
51049 var APIEndPoint = (function () {
51050 function APIEndPoint($resource, $http) {
51051 this.$resource = $resource;
51052 this.$http = $http;
51054 APIEndPoint.prototype.resource = function (endPoint, data) {
51055 var customAction = {
51061 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
51063 return this.$resource(endPoint, {}, { execute: execute });
51065 APIEndPoint.prototype.getOptionControlFile = function (command) {
51066 var endPoint = '/api/v1/optionControlFile/' + command;
51067 return this.resource(endPoint, {}).get();
51069 APIEndPoint.prototype.getFiles = function (fileId) {
51070 var endPoint = '/api/v1/workspace';
51072 endPoint += '/' + fileId;
51074 return this.resource(endPoint, {}).get();
51076 APIEndPoint.prototype.getDirectories = function () {
51077 var endPoint = '/api/v1/all/workspace/directory';
51078 return this.resource(endPoint, {}).get();
51080 APIEndPoint.prototype.getTags = function () {
51081 var endPoint = '/api/v1/tagList';
51082 return this.resource(endPoint, {}).get();
51084 APIEndPoint.prototype.getCommands = function () {
51085 var endPoint = '/api/v1/commandList';
51086 return this.resource(endPoint, {}).get();
51088 APIEndPoint.prototype.execute = function (data) {
51089 var endPoint = '/api/v1/execution';
51090 var fd = new FormData();
51091 fd.append('data', data);
51092 return this.$http.post(endPoint, fd, {
51093 headers: { 'Content-Type': undefined },
51094 transformRequest: angular.identity
51097 APIEndPoint.prototype.debug = function () {
51098 var endPoint = '/api/v1/debug';
51099 return this.$http.get(endPoint);
51102 APIEndPoint.prototype.upload = function () {
51103 var endPoint = '/api/v1/upload';
51104 return this.$http.get(endPoint);
51107 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51108 APIEndPoint.prototype.help = function (command) {
51109 var endPoint = '/api/v1/help/' + command;
51110 return this.$http.get(endPoint);
51112 return APIEndPoint;
51114 services.APIEndPoint = APIEndPoint;
51115 })(services = app.services || (app.services = {}));
51116 })(app || (app = {}));
51120 (function (services) {
51121 var MyModal = (function () {
51122 function MyModal($uibModal) {
51123 this.$uibModal = $uibModal;
51124 this.modalOption = {
51131 MyModal.prototype.open = function (modalName) {
51132 if (modalName === 'SelectCommand') {
51133 this.modalOption.templateUrl = 'templates/select-command.html';
51134 this.modalOption.size = 'lg';
51136 return this.$uibModal.open(this.modalOption);
51138 MyModal.prototype.selectCommand = function () {
51139 this.modalOption.templateUrl = 'templates/select-command.html';
51140 this.modalOption.controller = 'selectCommandController';
51141 this.modalOption.controllerAs = 'c';
51142 this.modalOption.size = 'lg';
51143 return this.$uibModal.open(this.modalOption);
51145 MyModal.prototype.preview = function () {
51146 this.modalOption.templateUrl = 'templates/preview.html';
51147 this.modalOption.controller = 'previewController';
51148 this.modalOption.controllerAs = 'c';
51149 this.modalOption.size = 'lg';
51150 return this.$uibModal.open(this.modalOption);
51153 MyModal.prototype.upload = function () {
51154 this.modalOption.templateUrl = 'templates/upload.html';
51155 this.modalOption.controller = 'uploadController';
51156 this.modalOption.controllerAs = 'c';
51157 this.modalOption.size = 'lg';
51158 return this.$uibModal.open(this.modalOption);
51161 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51162 MyModal.$inject = ['$uibModal'];
51165 services.MyModal = MyModal;
51166 })(services = app.services || (app.services = {}));
51167 })(app || (app = {}));
51171 (function (services) {
51172 var WebSocket = (function () {
51173 function WebSocket($rootScope) {
51174 this.$rootScope = $rootScope;
51175 this.socket = io.connect();
51177 WebSocket.prototype.on = function (eventName, callback) {
51178 var socket = this.socket;
51179 var rootScope = this.$rootScope;
51180 socket.on(eventName, function () {
51181 var args = arguments;
51182 rootScope.$apply(function () {
51183 callback.apply(socket, args);
51187 WebSocket.prototype.emit = function (eventName, data, callback) {
51188 var socket = this.socket;
51189 var rootScope = this.$rootScope;
51190 this.socket.emit(eventName, data, function () {
51191 var args = arguments;
51192 rootScope.$apply(function () {
51194 callback.apply(socket, args);
51200 services.WebSocket = WebSocket;
51201 })(services = app.services || (app.services = {}));
51202 })(app || (app = {}));
51206 (function (services) {
51207 var Console = (function () {
51208 function Console(WebSocket, $rootScope) {
51209 this.WebSocket = WebSocket;
51210 this.$rootScope = $rootScope;
51211 this.WebSocket = WebSocket;
51212 this.$rootScope = $rootScope;
51213 this.directiveIDs = [];
51214 var directiveIDs = this.directiveIDs;
51215 this.WebSocket.on('console', function (d) {
51217 var message = d.message;
51218 if (directiveIDs.indexOf(id) > -1) {
51219 $rootScope.$emit(id, message);
51223 Console.prototype.addDirective = function (id) {
51224 if (!(this.directiveIDs.indexOf(id) > -1)) {
51225 this.directiveIDs.push(id);
51228 Console.prototype.removeDirective = function (id) {
51229 var i = this.directiveIDs.indexOf(id);
51231 this.directiveIDs.splice(i, 1);
51234 Console.prototype.showIDs = function () {
51235 console.log(this.directiveIDs);
51239 services.Console = Console;
51240 })(services = app.services || (app.services = {}));
51241 })(app || (app = {}));
51245 (function (directives) {
51246 var Command = (function () {
51247 function Command() {
51248 this.restrict = 'E';
51249 this.replace = true;
51251 this.controller = 'commandController';
51252 this.controllerAs = 'ctrl';
51253 this.bindToController = {
51259 this.templateUrl = 'templates/command.html';
51261 Command.Factory = function () {
51262 var directive = function () {
51263 return new Command();
51265 directive.$inject = [];
51270 directives.Command = Command;
51271 var CommandController = (function () {
51272 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
51273 this.APIEndPoint = APIEndPoint;
51274 this.$scope = $scope;
51275 this.MyModal = MyModal;
51276 this.WebSocket = WebSocket;
51277 this.$window = $window;
51278 this.$rootScope = $rootScope;
51279 this.Console = Console;
51280 var controller = this;
51282 .getOptionControlFile(this.name)
51284 .then(function (result) {
51285 controller.options = result.info;
51290 .then(function (result) {
51291 controller.dirs = result.info;
51293 this.heading = "[" + this.index + "]: dcdFilePrint";
51294 this.isOpen = true;
51295 this.$scope.$on('close', function () {
51296 controller.isOpen = false;
51300 return Math.floor((1 + Math.random()) * 0x10000)
51304 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
51305 s4() + '-' + s4() + s4() + s4();
51307 this.uuid = guid();
51308 this.Console.addDirective(this.uuid);
51309 this.Console.showIDs();
51311 CommandController.prototype.submit = function () {
51313 angular.forEach(this.options, function (option) {
51315 name: option.option,
51318 angular.forEach(option.arg, function (arg) {
51320 if (typeof arg.input === 'object') {
51321 obj.arguments.push(arg.input.name);
51324 obj.arguments.push(arg.input);
51328 if (obj.arguments.length > 0) {
51333 command: this.name,
51334 workspace: this.workspace.fileId,
51338 .execute(JSON.stringify(execObj))
51339 .then(function (result) {
51340 console.log(result);
51343 CommandController.prototype.removeMySelf = function (index) {
51344 this.$scope.$destroy();
51345 this.Console.removeDirective(this.uuid);
51346 this.remove()(index, this.list);
51347 this.Console.showIDs();
51349 CommandController.prototype.reloadFiles = function () {
51351 var fileId = this.workspace.fileId;
51355 .then(function (result) {
51356 var status = result.status;
51357 if (status === 'success') {
51358 _this.files = result.info;
51361 console.log(result.message);
51365 CommandController.prototype.debug = function () {
51366 var div = angular.element(this.$window.document).find("div");
51369 angular.forEach(div, function (v) {
51370 if (v.className === "panel-body console") {
51373 else if (v.className === "row parameters-console") {
51377 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
51378 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
51379 consoleTag.style.height = consoleHeight;
51380 consoleTag.style.width = consoleWidth;
51382 CommandController.prototype.help = function () {
51385 .then(function (result) {
51386 console.log(result);
51389 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
51390 return CommandController;
51392 directives.CommandController = CommandController;
51393 })(directives = app.directives || (app.directives = {}));
51394 })(app || (app = {}));
51398 (function (directives) {
51399 var HeaderMenu = (function () {
51400 function HeaderMenu() {
51401 this.restrict = 'E';
51402 this.replace = true;
51403 this.templateUrl = 'templates/header-menu.html';
51404 this.controller = 'HeaderMenuController';
51405 this.controllerAs = 'hmc';
51408 HeaderMenu.Factory = function () {
51409 var directive = function () {
51410 return new HeaderMenu();
51416 directives.HeaderMenu = HeaderMenu;
51417 var HeaderMenuController = (function () {
51418 function HeaderMenuController($state) {
51419 this.$state = $state;
51420 this.isExecution = this.$state.current.name === 'execution';
51421 this.isWorkspace = this.$state.current.name === 'workspace';
51422 this.isHistory = this.$state.current.name === 'history';
51424 HeaderMenuController.prototype.transit = function (state) {
51425 this.$state.go(state);
51427 HeaderMenuController.$inject = ['$state'];
51428 return HeaderMenuController;
51430 directives.HeaderMenuController = HeaderMenuController;
51431 })(directives = app.directives || (app.directives = {}));
51432 })(app || (app = {}));
51436 (function (directives) {
51437 var Option = (function () {
51438 function Option() {
51439 this.restrict = 'E';
51440 this.replace = true;
51441 this.controller = 'optionController';
51442 this.bindToController = {
51447 this.templateUrl = 'templates/option.html';
51448 this.controllerAs = 'ctrl';
51450 Option.Factory = function () {
51451 var directive = function () {
51452 return new Option();
51454 directive.$inject = [];
51459 directives.Option = Option;
51460 var OptionController = (function () {
51461 function OptionController() {
51462 var controller = this;
51463 angular.forEach(controller.info.arg, function (arg) {
51464 if (arg.initialValue) {
51465 if (arg.formType === 'number') {
51466 arg.input = parseInt(arg.initialValue);
51469 arg.input = arg.initialValue;
51474 OptionController.$inject = [];
51475 return OptionController;
51477 directives.OptionController = OptionController;
51478 })(directives = app.directives || (app.directives = {}));
51479 })(app || (app = {}));
51483 (function (directives) {
51484 var Directory = (function () {
51485 function Directory() {
51486 this.restrict = 'E';
51487 this.replace = true;
51488 this.controller = 'directoryController';
51489 this.controllerAs = 'ctrl';
51490 this.bindToController = {
51496 this.templateUrl = 'templates/directory.html';
51498 Directory.Factory = function () {
51499 var directive = function () {
51500 return new Directory();
51506 directives.Directory = Directory;
51507 var DirectoryController = (function () {
51508 function DirectoryController(APIEndPoint, $scope) {
51509 this.APIEndPoint = APIEndPoint;
51510 this.$scope = $scope;
51511 var controller = this;
51513 .getFiles(this.info.fileId)
51515 .then(function (result) {
51516 if (result.status === 'success') {
51517 controller.files = result.info;
51518 angular.forEach(result.info, function (file) {
51519 if (file.fileType === '0') {
51521 if (controller.info.path === '/') {
51522 o.path = '/' + file.name;
51525 o.path = controller.info.path + '/' + file.name;
51527 controller.add()(o, controller.list);
51534 DirectoryController.$inject = ['APIEndPoint', '$scope'];
51535 return DirectoryController;
51537 directives.DirectoryController = DirectoryController;
51538 })(directives = app.directives || (app.directives = {}));
51539 })(app || (app = {}));
51544 (function (directives) {
51545 var Upload = (function () {
51546 function Upload() {
51547 this.restrict = 'E';
51548 this.replace = true;
51550 this.controller = 'UploadController';
51551 this.controllerAs = 'ctrl';
51552 this.bindToController = {
51558 this.templateUrl = 'templates/upload.html';
51559 console.log("templates/upload.html-constructor");
51561 Upload.Factory = function () {
51562 var directive = function () {
51563 return new Upload();
51565 directive.$inject = [];
51570 directives.Upload = Upload;
51571 var UploadController = (function () {
51572 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
51573 this.APIEndPoint = APIEndPoint;
51574 this.$scope = $scope;
51575 this.MyModal = MyModal;
51576 this.WebSocket = WebSocket;
51577 this.$window = $window;
51578 this.$rootScope = $rootScope;
51579 this.Console = Console;
51580 var controller = this;
51581 console.log("directive.upload-constructor");
51583 UploadController.prototype.submit = function () {
51584 console.log("submit: function not supported¥n");
51586 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
51587 return UploadController;
51589 directives.UploadController = UploadController;
51590 })(directives = app.directives || (app.directives = {}));
51591 })(app || (app = {}));
51595 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51597 (function (controllers) {
51598 var Execution = (function () {
51599 function Execution(MyModal, $scope) {
51600 this.MyModal = MyModal;
51601 this.$scope = $scope;
51602 this.commandInfoList = [];
51605 Execution.prototype.add = function () {
51606 this.$scope.$broadcast('close');
51607 var commandInfoList = this.commandInfoList;
51608 var commandInstance = this.MyModal.selectCommand();
51611 .then(function (command) {
51612 commandInfoList.push(new app.declares.CommandInfo(command));
51615 Execution.prototype.open = function () {
51616 var result = this.MyModal.open('SelectCommand');
51617 console.log(result);
51619 Execution.prototype.remove = function (index, list) {
51620 list.splice(index, 1);
51622 Execution.prototype.close = function () {
51623 console.log("close");
51625 Execution.$inject = ['MyModal', '$scope'];
51628 controllers.Execution = Execution;
51629 })(controllers = app.controllers || (app.controllers = {}));
51630 })(app || (app = {}));
51634 (function (controllers) {
51635 var Workspace = (function () {
51636 function Workspace($scope, APIEndPoint, MyModal) {
51637 this.$scope = $scope;
51638 this.APIEndPoint = APIEndPoint;
51639 this.MyModal = MyModal;
51640 this.directoryList = [];
51641 var controller = this;
51642 var directoryList = this.directoryList;
51644 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
51652 directoryList.push(o);
51654 Workspace.prototype.addDirectory = function (info, directoryList) {
51655 directoryList.push(info);
51658 Workspace.prototype.upload = function () {
51659 this.MyModal.upload();
51662 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51663 Workspace.prototype.debug = function () {
51664 this.MyModal.preview();
51666 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
51669 controllers.Workspace = Workspace;
51670 })(controllers = app.controllers || (app.controllers = {}));
51671 })(app || (app = {}));
51675 (function (controllers) {
51676 var History = (function () {
51677 function History($scope) {
51678 this.page = "History";
51680 History.$inject = ['$scope'];
51683 controllers.History = History;
51684 })(controllers = app.controllers || (app.controllers = {}));
51685 })(app || (app = {}));
51689 (function (controllers) {
51690 var SelectCommand = (function () {
51691 function SelectCommand($scope, APIEndPoint, $modalInstance) {
51692 this.APIEndPoint = APIEndPoint;
51693 this.$modalInstance = $modalInstance;
51694 var controller = this;
51697 .$promise.then(function (result) {
51698 controller.tags = result.info;
51702 .$promise.then(function (result) {
51703 controller.commands = result.info;
51705 this.currentTag = 'all';
51707 SelectCommand.prototype.changeTag = function (tag) {
51708 this.currentTag = tag;
51710 SelectCommand.prototype.selectCommand = function (command) {
51711 this.$modalInstance.close(command);
51713 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
51714 return SelectCommand;
51716 controllers.SelectCommand = SelectCommand;
51717 })(controllers = app.controllers || (app.controllers = {}));
51718 })(app || (app = {}));
51722 (function (controllers) {
51724 var Upload = (function () {
51725 function Upload($scope, APIEndPoint, $modalInstance) {
51726 this.APIEndPoint = APIEndPoint;
51727 this.$modalInstance = $modalInstance;
51728 var controller = this;
51729 console.log('controller.upload-controllers');
51731 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
51734 controllers.Upload = Upload;
51735 })(controllers = app.controllers || (app.controllers = {}));
51736 })(app || (app = {}));
51740 (function (controllers) {
51742 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51743 var Preview = (function () {
51744 function Preview($scope, APIEndPoint, $modalInstance) {
51745 this.APIEndPoint = APIEndPoint;
51746 this.$modalInstance = $modalInstance;
51747 var controller = this;
51748 console.log('preview');
51750 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
51753 controllers.Preview = Preview;
51754 })(controllers = app.controllers || (app.controllers = {}));
51755 })(app || (app = {}));
51757 (function (filters) {
51759 return function (commands, tag) {
51761 angular.forEach(commands, function (command) {
51763 angular.forEach(command.tags, function (value) {
51768 result.push(command);
51774 })(filters || (filters = {}));
51778 var appName = 'zephyr';
51779 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
51780 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
51781 $urlRouterProvider.otherwise('/execution');
51782 $locationProvider.html5Mode({
51787 .state('execution', {
51789 templateUrl: 'templates/execution.html',
51790 controller: 'executionController',
51793 .state('workspace', {
51795 templateUrl: 'templates/workspace.html',
51796 controller: 'workspaceController',
51799 .state('history', {
51801 templateUrl: 'templates/history.html',
51802 controller: 'historyController',
51806 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
51807 app.zephyr.service('MyModal', app.services.MyModal);
51808 app.zephyr.service('WebSocket', app.services.WebSocket);
51809 app.zephyr.service('Console', app.services.Console);
51810 app.zephyr.filter('Tag', filters.Tag);
51811 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
51812 app.zephyr.controller('previewController', app.controllers.Preview);
51814 app.zephyr.controller('uploadController', app.controllers.Upload);
51816 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51817 app.zephyr.controller('executionController', app.controllers.Execution);
51818 app.zephyr.controller('workspaceController', app.controllers.Workspace);
51819 app.zephyr.controller('historyController', app.controllers.History);
51820 app.zephyr.controller('commandController', app.directives.CommandController);
51821 app.zephyr.controller('optionController', app.directives.OptionController);
51822 app.zephyr.controller('directoryController', app.directives.DirectoryController);
51823 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
51825 app.zephyr.controller('uploadController', app.directives.UploadController);
51827 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51828 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
51829 app.zephyr.directive('command', app.directives.Command.Factory());
51830 app.zephyr.directive('option', app.directives.Option.Factory());
51831 app.zephyr.directive('directory', app.directives.Directory.Factory());
51832 })(app || (app = {}));
51840 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51841 /***/ function(module, exports) {
51846 (function (declares) {
51847 var CommandInfo = (function () {
51848 function CommandInfo(name) {
51851 return CommandInfo;
51853 declares.CommandInfo = CommandInfo;
51854 })(declares = app.declares || (app.declares = {}));
51855 })(app || (app = {}));
51859 (function (services) {
51860 var APIEndPoint = (function () {
51861 function APIEndPoint($resource, $http) {
51862 this.$resource = $resource;
51863 this.$http = $http;
51865 APIEndPoint.prototype.resource = function (endPoint, data) {
51866 var customAction = {
51872 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
51874 return this.$resource(endPoint, {}, { execute: execute });
51876 APIEndPoint.prototype.getOptionControlFile = function (command) {
51877 var endPoint = '/api/v1/optionControlFile/' + command;
51878 return this.resource(endPoint, {}).get();
51880 APIEndPoint.prototype.getFiles = function (fileId) {
51881 var endPoint = '/api/v1/workspace';
51883 endPoint += '/' + fileId;
51885 return this.resource(endPoint, {}).get();
51887 APIEndPoint.prototype.getDirectories = function () {
51888 var endPoint = '/api/v1/all/workspace/directory';
51889 return this.resource(endPoint, {}).get();
51891 APIEndPoint.prototype.getTags = function () {
51892 var endPoint = '/api/v1/tagList';
51893 return this.resource(endPoint, {}).get();
51895 APIEndPoint.prototype.getCommands = function () {
51896 var endPoint = '/api/v1/commandList';
51897 return this.resource(endPoint, {}).get();
51899 APIEndPoint.prototype.execute = function (data) {
51900 var endPoint = '/api/v1/execution';
51901 var fd = new FormData();
51902 fd.append('data', data);
51903 return this.$http.post(endPoint, fd, {
51904 headers: { 'Content-Type': undefined },
51905 transformRequest: angular.identity
51908 APIEndPoint.prototype.debug = function () {
51909 var endPoint = '/api/v1/debug';
51910 return this.$http.get(endPoint);
51913 APIEndPoint.prototype.upload = function () {
51914 var endPoint = '/api/v1/upload';
51915 return this.$http.get(endPoint);
51918 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51919 APIEndPoint.prototype.help = function (command) {
51920 var endPoint = '/api/v1/help/' + command;
51921 return this.$http.get(endPoint);
51923 return APIEndPoint;
51925 services.APIEndPoint = APIEndPoint;
51926 })(services = app.services || (app.services = {}));
51927 })(app || (app = {}));
51931 (function (services) {
51932 var MyModal = (function () {
51933 function MyModal($uibModal) {
51934 this.$uibModal = $uibModal;
51935 this.modalOption = {
51942 MyModal.prototype.open = function (modalName) {
51943 if (modalName === 'SelectCommand') {
51944 this.modalOption.templateUrl = 'templates/select-command.html';
51945 this.modalOption.size = 'lg';
51947 return this.$uibModal.open(this.modalOption);
51949 MyModal.prototype.selectCommand = function () {
51950 this.modalOption.templateUrl = 'templates/select-command.html';
51951 this.modalOption.controller = 'selectCommandController';
51952 this.modalOption.controllerAs = 'c';
51953 this.modalOption.size = 'lg';
51954 return this.$uibModal.open(this.modalOption);
51956 MyModal.prototype.preview = function () {
51957 this.modalOption.templateUrl = 'templates/preview.html';
51958 this.modalOption.controller = 'previewController';
51959 this.modalOption.controllerAs = 'c';
51960 this.modalOption.size = 'lg';
51961 return this.$uibModal.open(this.modalOption);
51964 MyModal.prototype.upload = function () {
51965 this.modalOption.templateUrl = 'templates/upload.html';
51966 this.modalOption.controller = 'uploadController';
51967 this.modalOption.controllerAs = 'c';
51968 this.modalOption.size = 'lg';
51969 return this.$uibModal.open(this.modalOption);
51972 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
51973 MyModal.$inject = ['$uibModal'];
51976 services.MyModal = MyModal;
51977 })(services = app.services || (app.services = {}));
51978 })(app || (app = {}));
51982 (function (services) {
51983 var WebSocket = (function () {
51984 function WebSocket($rootScope) {
51985 this.$rootScope = $rootScope;
51986 this.socket = io.connect();
51988 WebSocket.prototype.on = function (eventName, callback) {
51989 var socket = this.socket;
51990 var rootScope = this.$rootScope;
51991 socket.on(eventName, function () {
51992 var args = arguments;
51993 rootScope.$apply(function () {
51994 callback.apply(socket, args);
51998 WebSocket.prototype.emit = function (eventName, data, callback) {
51999 var socket = this.socket;
52000 var rootScope = this.$rootScope;
52001 this.socket.emit(eventName, data, function () {
52002 var args = arguments;
52003 rootScope.$apply(function () {
52005 callback.apply(socket, args);
52011 services.WebSocket = WebSocket;
52012 })(services = app.services || (app.services = {}));
52013 })(app || (app = {}));
52017 (function (services) {
52018 var Console = (function () {
52019 function Console(WebSocket, $rootScope) {
52020 this.WebSocket = WebSocket;
52021 this.$rootScope = $rootScope;
52022 this.WebSocket = WebSocket;
52023 this.$rootScope = $rootScope;
52024 this.directiveIDs = [];
52025 var directiveIDs = this.directiveIDs;
52026 this.WebSocket.on('console', function (d) {
52028 var message = d.message;
52029 if (directiveIDs.indexOf(id) > -1) {
52030 $rootScope.$emit(id, message);
52034 Console.prototype.addDirective = function (id) {
52035 if (!(this.directiveIDs.indexOf(id) > -1)) {
52036 this.directiveIDs.push(id);
52039 Console.prototype.removeDirective = function (id) {
52040 var i = this.directiveIDs.indexOf(id);
52042 this.directiveIDs.splice(i, 1);
52045 Console.prototype.showIDs = function () {
52046 console.log(this.directiveIDs);
52050 services.Console = Console;
52051 })(services = app.services || (app.services = {}));
52052 })(app || (app = {}));
52056 (function (directives) {
52057 var Command = (function () {
52058 function Command() {
52059 this.restrict = 'E';
52060 this.replace = true;
52062 this.controller = 'commandController';
52063 this.controllerAs = 'ctrl';
52064 this.bindToController = {
52070 this.templateUrl = 'templates/command.html';
52072 Command.Factory = function () {
52073 var directive = function () {
52074 return new Command();
52076 directive.$inject = [];
52081 directives.Command = Command;
52082 var CommandController = (function () {
52083 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
52084 this.APIEndPoint = APIEndPoint;
52085 this.$scope = $scope;
52086 this.MyModal = MyModal;
52087 this.WebSocket = WebSocket;
52088 this.$window = $window;
52089 this.$rootScope = $rootScope;
52090 this.Console = Console;
52091 var controller = this;
52093 .getOptionControlFile(this.name)
52095 .then(function (result) {
52096 controller.options = result.info;
52101 .then(function (result) {
52102 controller.dirs = result.info;
52104 this.heading = "[" + this.index + "]: dcdFilePrint";
52105 this.isOpen = true;
52106 this.$scope.$on('close', function () {
52107 controller.isOpen = false;
52111 return Math.floor((1 + Math.random()) * 0x10000)
52115 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
52116 s4() + '-' + s4() + s4() + s4();
52118 this.uuid = guid();
52119 this.Console.addDirective(this.uuid);
52120 this.Console.showIDs();
52122 CommandController.prototype.submit = function () {
52124 angular.forEach(this.options, function (option) {
52126 name: option.option,
52129 angular.forEach(option.arg, function (arg) {
52131 if (typeof arg.input === 'object') {
52132 obj.arguments.push(arg.input.name);
52135 obj.arguments.push(arg.input);
52139 if (obj.arguments.length > 0) {
52144 command: this.name,
52145 workspace: this.workspace.fileId,
52149 .execute(JSON.stringify(execObj))
52150 .then(function (result) {
52151 console.log(result);
52154 CommandController.prototype.removeMySelf = function (index) {
52155 this.$scope.$destroy();
52156 this.Console.removeDirective(this.uuid);
52157 this.remove()(index, this.list);
52158 this.Console.showIDs();
52160 CommandController.prototype.reloadFiles = function () {
52162 var fileId = this.workspace.fileId;
52166 .then(function (result) {
52167 var status = result.status;
52168 if (status === 'success') {
52169 _this.files = result.info;
52172 console.log(result.message);
52176 CommandController.prototype.debug = function () {
52177 var div = angular.element(this.$window.document).find("div");
52180 angular.forEach(div, function (v) {
52181 if (v.className === "panel-body console") {
52184 else if (v.className === "row parameters-console") {
52188 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
52189 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
52190 consoleTag.style.height = consoleHeight;
52191 consoleTag.style.width = consoleWidth;
52193 CommandController.prototype.help = function () {
52196 .then(function (result) {
52197 console.log(result);
52200 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
52201 return CommandController;
52203 directives.CommandController = CommandController;
52204 })(directives = app.directives || (app.directives = {}));
52205 })(app || (app = {}));
52209 (function (directives) {
52210 var HeaderMenu = (function () {
52211 function HeaderMenu() {
52212 this.restrict = 'E';
52213 this.replace = true;
52214 this.templateUrl = 'templates/header-menu.html';
52215 this.controller = 'HeaderMenuController';
52216 this.controllerAs = 'hmc';
52219 HeaderMenu.Factory = function () {
52220 var directive = function () {
52221 return new HeaderMenu();
52227 directives.HeaderMenu = HeaderMenu;
52228 var HeaderMenuController = (function () {
52229 function HeaderMenuController($state) {
52230 this.$state = $state;
52231 this.isExecution = this.$state.current.name === 'execution';
52232 this.isWorkspace = this.$state.current.name === 'workspace';
52233 this.isHistory = this.$state.current.name === 'history';
52235 HeaderMenuController.prototype.transit = function (state) {
52236 this.$state.go(state);
52238 HeaderMenuController.$inject = ['$state'];
52239 return HeaderMenuController;
52241 directives.HeaderMenuController = HeaderMenuController;
52242 })(directives = app.directives || (app.directives = {}));
52243 })(app || (app = {}));
52247 (function (directives) {
52248 var Option = (function () {
52249 function Option() {
52250 this.restrict = 'E';
52251 this.replace = true;
52252 this.controller = 'optionController';
52253 this.bindToController = {
52258 this.templateUrl = 'templates/option.html';
52259 this.controllerAs = 'ctrl';
52261 Option.Factory = function () {
52262 var directive = function () {
52263 return new Option();
52265 directive.$inject = [];
52270 directives.Option = Option;
52271 var OptionController = (function () {
52272 function OptionController() {
52273 var controller = this;
52274 angular.forEach(controller.info.arg, function (arg) {
52275 if (arg.initialValue) {
52276 if (arg.formType === 'number') {
52277 arg.input = parseInt(arg.initialValue);
52280 arg.input = arg.initialValue;
52285 OptionController.$inject = [];
52286 return OptionController;
52288 directives.OptionController = OptionController;
52289 })(directives = app.directives || (app.directives = {}));
52290 })(app || (app = {}));
52294 (function (directives) {
52295 var Directory = (function () {
52296 function Directory() {
52297 this.restrict = 'E';
52298 this.replace = true;
52299 this.controller = 'directoryController';
52300 this.controllerAs = 'ctrl';
52301 this.bindToController = {
52307 this.templateUrl = 'templates/directory.html';
52309 Directory.Factory = function () {
52310 var directive = function () {
52311 return new Directory();
52317 directives.Directory = Directory;
52318 var DirectoryController = (function () {
52319 function DirectoryController(APIEndPoint, $scope) {
52320 this.APIEndPoint = APIEndPoint;
52321 this.$scope = $scope;
52322 var controller = this;
52324 .getFiles(this.info.fileId)
52326 .then(function (result) {
52327 if (result.status === 'success') {
52328 controller.files = result.info;
52329 angular.forEach(result.info, function (file) {
52330 if (file.fileType === '0') {
52332 if (controller.info.path === '/') {
52333 o.path = '/' + file.name;
52336 o.path = controller.info.path + '/' + file.name;
52338 controller.add()(o, controller.list);
52345 DirectoryController.$inject = ['APIEndPoint', '$scope'];
52346 return DirectoryController;
52348 directives.DirectoryController = DirectoryController;
52349 })(directives = app.directives || (app.directives = {}));
52350 })(app || (app = {}));
52355 (function (directives) {
52356 var Upload = (function () {
52357 function Upload() {
52358 this.restrict = 'E';
52359 this.replace = true;
52361 this.controller = 'UploadController';
52362 this.controllerAs = 'ctrl';
52363 this.bindToController = {
52369 this.templateUrl = 'templates/upload.html';
52370 console.log("templates/upload.html-constructor");
52372 Upload.Factory = function () {
52373 var directive = function () {
52374 return new Upload();
52376 directive.$inject = [];
52381 directives.Upload = Upload;
52382 var UploadController = (function () {
52383 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
52384 this.APIEndPoint = APIEndPoint;
52385 this.$scope = $scope;
52386 this.MyModal = MyModal;
52387 this.WebSocket = WebSocket;
52388 this.$window = $window;
52389 this.$rootScope = $rootScope;
52390 this.Console = Console;
52391 var controller = this;
52392 console.log("directive.upload-constructor");
52394 UploadController.prototype.submit = function () {
52395 console.log("submit: function not supported¥n");
52397 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
52398 return UploadController;
52400 directives.UploadController = UploadController;
52401 })(directives = app.directives || (app.directives = {}));
52402 })(app || (app = {}));
52406 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52408 (function (controllers) {
52409 var Execution = (function () {
52410 function Execution(MyModal, $scope) {
52411 this.MyModal = MyModal;
52412 this.$scope = $scope;
52413 this.commandInfoList = [];
52416 Execution.prototype.add = function () {
52417 this.$scope.$broadcast('close');
52418 var commandInfoList = this.commandInfoList;
52419 var commandInstance = this.MyModal.selectCommand();
52422 .then(function (command) {
52423 commandInfoList.push(new app.declares.CommandInfo(command));
52426 Execution.prototype.open = function () {
52427 var result = this.MyModal.open('SelectCommand');
52428 console.log(result);
52430 Execution.prototype.remove = function (index, list) {
52431 list.splice(index, 1);
52433 Execution.prototype.close = function () {
52434 console.log("close");
52436 Execution.$inject = ['MyModal', '$scope'];
52439 controllers.Execution = Execution;
52440 })(controllers = app.controllers || (app.controllers = {}));
52441 })(app || (app = {}));
52445 (function (controllers) {
52446 var Workspace = (function () {
52447 function Workspace($scope, APIEndPoint, MyModal) {
52448 this.$scope = $scope;
52449 this.APIEndPoint = APIEndPoint;
52450 this.MyModal = MyModal;
52451 this.directoryList = [];
52452 var controller = this;
52453 var directoryList = this.directoryList;
52455 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
52463 directoryList.push(o);
52465 Workspace.prototype.addDirectory = function (info, directoryList) {
52466 directoryList.push(info);
52469 Workspace.prototype.upload = function () {
52470 this.MyModal.upload();
52473 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52474 Workspace.prototype.debug = function () {
52475 this.MyModal.preview();
52477 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
52480 controllers.Workspace = Workspace;
52481 })(controllers = app.controllers || (app.controllers = {}));
52482 })(app || (app = {}));
52486 (function (controllers) {
52487 var History = (function () {
52488 function History($scope) {
52489 this.page = "History";
52491 History.$inject = ['$scope'];
52494 controllers.History = History;
52495 })(controllers = app.controllers || (app.controllers = {}));
52496 })(app || (app = {}));
52500 (function (controllers) {
52501 var SelectCommand = (function () {
52502 function SelectCommand($scope, APIEndPoint, $modalInstance) {
52503 this.APIEndPoint = APIEndPoint;
52504 this.$modalInstance = $modalInstance;
52505 var controller = this;
52508 .$promise.then(function (result) {
52509 controller.tags = result.info;
52513 .$promise.then(function (result) {
52514 controller.commands = result.info;
52516 this.currentTag = 'all';
52518 SelectCommand.prototype.changeTag = function (tag) {
52519 this.currentTag = tag;
52521 SelectCommand.prototype.selectCommand = function (command) {
52522 this.$modalInstance.close(command);
52524 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
52525 return SelectCommand;
52527 controllers.SelectCommand = SelectCommand;
52528 })(controllers = app.controllers || (app.controllers = {}));
52529 })(app || (app = {}));
52533 (function (controllers) {
52535 var Upload = (function () {
52536 function Upload($scope, APIEndPoint, $modalInstance) {
52537 this.APIEndPoint = APIEndPoint;
52538 this.$modalInstance = $modalInstance;
52539 var controller = this;
52540 console.log('controller.upload-controllers');
52542 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
52545 controllers.Upload = Upload;
52546 })(controllers = app.controllers || (app.controllers = {}));
52547 })(app || (app = {}));
52551 (function (controllers) {
52553 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52554 var Preview = (function () {
52555 function Preview($scope, APIEndPoint, $modalInstance) {
52556 this.APIEndPoint = APIEndPoint;
52557 this.$modalInstance = $modalInstance;
52558 var controller = this;
52559 console.log('preview');
52561 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
52564 controllers.Preview = Preview;
52565 })(controllers = app.controllers || (app.controllers = {}));
52566 })(app || (app = {}));
52568 (function (filters) {
52570 return function (commands, tag) {
52572 angular.forEach(commands, function (command) {
52574 angular.forEach(command.tags, function (value) {
52579 result.push(command);
52585 })(filters || (filters = {}));
52589 var appName = 'zephyr';
52590 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
52591 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
52592 $urlRouterProvider.otherwise('/execution');
52593 $locationProvider.html5Mode({
52598 .state('execution', {
52600 templateUrl: 'templates/execution.html',
52601 controller: 'executionController',
52604 .state('workspace', {
52606 templateUrl: 'templates/workspace.html',
52607 controller: 'workspaceController',
52610 .state('history', {
52612 templateUrl: 'templates/history.html',
52613 controller: 'historyController',
52617 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
52618 app.zephyr.service('MyModal', app.services.MyModal);
52619 app.zephyr.service('WebSocket', app.services.WebSocket);
52620 app.zephyr.service('Console', app.services.Console);
52621 app.zephyr.filter('Tag', filters.Tag);
52622 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
52623 app.zephyr.controller('previewController', app.controllers.Preview);
52625 app.zephyr.controller('uploadController', app.controllers.Upload);
52627 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52628 app.zephyr.controller('executionController', app.controllers.Execution);
52629 app.zephyr.controller('workspaceController', app.controllers.Workspace);
52630 app.zephyr.controller('historyController', app.controllers.History);
52631 app.zephyr.controller('commandController', app.directives.CommandController);
52632 app.zephyr.controller('optionController', app.directives.OptionController);
52633 app.zephyr.controller('directoryController', app.directives.DirectoryController);
52634 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
52636 app.zephyr.controller('uploadController', app.directives.UploadController);
52638 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52639 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
52640 app.zephyr.directive('command', app.directives.Command.Factory());
52641 app.zephyr.directive('option', app.directives.Option.Factory());
52642 app.zephyr.directive('directory', app.directives.Directory.Factory());
52643 })(app || (app = {}));
52651 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52652 /***/ function(module, exports) {
52657 (function (declares) {
52658 var CommandInfo = (function () {
52659 function CommandInfo(name) {
52662 return CommandInfo;
52664 declares.CommandInfo = CommandInfo;
52665 })(declares = app.declares || (app.declares = {}));
52666 })(app || (app = {}));
52670 (function (services) {
52671 var APIEndPoint = (function () {
52672 function APIEndPoint($resource, $http) {
52673 this.$resource = $resource;
52674 this.$http = $http;
52676 APIEndPoint.prototype.resource = function (endPoint, data) {
52677 var customAction = {
52683 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
52685 return this.$resource(endPoint, {}, { execute: execute });
52687 APIEndPoint.prototype.getOptionControlFile = function (command) {
52688 var endPoint = '/api/v1/optionControlFile/' + command;
52689 return this.resource(endPoint, {}).get();
52691 APIEndPoint.prototype.getFiles = function (fileId) {
52692 var endPoint = '/api/v1/workspace';
52694 endPoint += '/' + fileId;
52696 return this.resource(endPoint, {}).get();
52698 APIEndPoint.prototype.getDirectories = function () {
52699 var endPoint = '/api/v1/all/workspace/directory';
52700 return this.resource(endPoint, {}).get();
52702 APIEndPoint.prototype.getTags = function () {
52703 var endPoint = '/api/v1/tagList';
52704 return this.resource(endPoint, {}).get();
52706 APIEndPoint.prototype.getCommands = function () {
52707 var endPoint = '/api/v1/commandList';
52708 return this.resource(endPoint, {}).get();
52710 APIEndPoint.prototype.execute = function (data) {
52711 var endPoint = '/api/v1/execution';
52712 var fd = new FormData();
52713 fd.append('data', data);
52714 return this.$http.post(endPoint, fd, {
52715 headers: { 'Content-Type': undefined },
52716 transformRequest: angular.identity
52719 APIEndPoint.prototype.debug = function () {
52720 var endPoint = '/api/v1/debug';
52721 return this.$http.get(endPoint);
52724 APIEndPoint.prototype.upload = function () {
52725 var endPoint = '/api/v1/upload';
52726 return this.$http.get(endPoint);
52729 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52730 APIEndPoint.prototype.help = function (command) {
52731 var endPoint = '/api/v1/help/' + command;
52732 return this.$http.get(endPoint);
52734 return APIEndPoint;
52736 services.APIEndPoint = APIEndPoint;
52737 })(services = app.services || (app.services = {}));
52738 })(app || (app = {}));
52742 (function (services) {
52743 var MyModal = (function () {
52744 function MyModal($uibModal) {
52745 this.$uibModal = $uibModal;
52746 this.modalOption = {
52753 MyModal.prototype.open = function (modalName) {
52754 if (modalName === 'SelectCommand') {
52755 this.modalOption.templateUrl = 'templates/select-command.html';
52756 this.modalOption.size = 'lg';
52758 return this.$uibModal.open(this.modalOption);
52760 MyModal.prototype.selectCommand = function () {
52761 this.modalOption.templateUrl = 'templates/select-command.html';
52762 this.modalOption.controller = 'selectCommandController';
52763 this.modalOption.controllerAs = 'c';
52764 this.modalOption.size = 'lg';
52765 return this.$uibModal.open(this.modalOption);
52767 MyModal.prototype.preview = function () {
52768 this.modalOption.templateUrl = 'templates/preview.html';
52769 this.modalOption.controller = 'previewController';
52770 this.modalOption.controllerAs = 'c';
52771 this.modalOption.size = 'lg';
52772 return this.$uibModal.open(this.modalOption);
52775 MyModal.prototype.upload = function () {
52776 this.modalOption.templateUrl = 'templates/upload.html';
52777 this.modalOption.controller = 'uploadController';
52778 this.modalOption.controllerAs = 'c';
52779 this.modalOption.size = 'lg';
52780 return this.$uibModal.open(this.modalOption);
52783 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
52784 MyModal.$inject = ['$uibModal'];
52787 services.MyModal = MyModal;
52788 })(services = app.services || (app.services = {}));
52789 })(app || (app = {}));
52793 (function (services) {
52794 var WebSocket = (function () {
52795 function WebSocket($rootScope) {
52796 this.$rootScope = $rootScope;
52797 this.socket = io.connect();
52799 WebSocket.prototype.on = function (eventName, callback) {
52800 var socket = this.socket;
52801 var rootScope = this.$rootScope;
52802 socket.on(eventName, function () {
52803 var args = arguments;
52804 rootScope.$apply(function () {
52805 callback.apply(socket, args);
52809 WebSocket.prototype.emit = function (eventName, data, callback) {
52810 var socket = this.socket;
52811 var rootScope = this.$rootScope;
52812 this.socket.emit(eventName, data, function () {
52813 var args = arguments;
52814 rootScope.$apply(function () {
52816 callback.apply(socket, args);
52822 services.WebSocket = WebSocket;
52823 })(services = app.services || (app.services = {}));
52824 })(app || (app = {}));
52828 (function (services) {
52829 var Console = (function () {
52830 function Console(WebSocket, $rootScope) {
52831 this.WebSocket = WebSocket;
52832 this.$rootScope = $rootScope;
52833 this.WebSocket = WebSocket;
52834 this.$rootScope = $rootScope;
52835 this.directiveIDs = [];
52836 var directiveIDs = this.directiveIDs;
52837 this.WebSocket.on('console', function (d) {
52839 var message = d.message;
52840 if (directiveIDs.indexOf(id) > -1) {
52841 $rootScope.$emit(id, message);
52845 Console.prototype.addDirective = function (id) {
52846 if (!(this.directiveIDs.indexOf(id) > -1)) {
52847 this.directiveIDs.push(id);
52850 Console.prototype.removeDirective = function (id) {
52851 var i = this.directiveIDs.indexOf(id);
52853 this.directiveIDs.splice(i, 1);
52856 Console.prototype.showIDs = function () {
52857 console.log(this.directiveIDs);
52861 services.Console = Console;
52862 })(services = app.services || (app.services = {}));
52863 })(app || (app = {}));
52867 (function (directives) {
52868 var Command = (function () {
52869 function Command() {
52870 this.restrict = 'E';
52871 this.replace = true;
52873 this.controller = 'commandController';
52874 this.controllerAs = 'ctrl';
52875 this.bindToController = {
52881 this.templateUrl = 'templates/command.html';
52883 Command.Factory = function () {
52884 var directive = function () {
52885 return new Command();
52887 directive.$inject = [];
52892 directives.Command = Command;
52893 var CommandController = (function () {
52894 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
52895 this.APIEndPoint = APIEndPoint;
52896 this.$scope = $scope;
52897 this.MyModal = MyModal;
52898 this.WebSocket = WebSocket;
52899 this.$window = $window;
52900 this.$rootScope = $rootScope;
52901 this.Console = Console;
52902 var controller = this;
52904 .getOptionControlFile(this.name)
52906 .then(function (result) {
52907 controller.options = result.info;
52912 .then(function (result) {
52913 controller.dirs = result.info;
52915 this.heading = "[" + this.index + "]: dcdFilePrint";
52916 this.isOpen = true;
52917 this.$scope.$on('close', function () {
52918 controller.isOpen = false;
52922 return Math.floor((1 + Math.random()) * 0x10000)
52926 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
52927 s4() + '-' + s4() + s4() + s4();
52929 this.uuid = guid();
52930 this.Console.addDirective(this.uuid);
52931 this.Console.showIDs();
52933 CommandController.prototype.submit = function () {
52935 angular.forEach(this.options, function (option) {
52937 name: option.option,
52940 angular.forEach(option.arg, function (arg) {
52942 if (typeof arg.input === 'object') {
52943 obj.arguments.push(arg.input.name);
52946 obj.arguments.push(arg.input);
52950 if (obj.arguments.length > 0) {
52955 command: this.name,
52956 workspace: this.workspace.fileId,
52960 .execute(JSON.stringify(execObj))
52961 .then(function (result) {
52962 console.log(result);
52965 CommandController.prototype.removeMySelf = function (index) {
52966 this.$scope.$destroy();
52967 this.Console.removeDirective(this.uuid);
52968 this.remove()(index, this.list);
52969 this.Console.showIDs();
52971 CommandController.prototype.reloadFiles = function () {
52973 var fileId = this.workspace.fileId;
52977 .then(function (result) {
52978 var status = result.status;
52979 if (status === 'success') {
52980 _this.files = result.info;
52983 console.log(result.message);
52987 CommandController.prototype.debug = function () {
52988 var div = angular.element(this.$window.document).find("div");
52991 angular.forEach(div, function (v) {
52992 if (v.className === "panel-body console") {
52995 else if (v.className === "row parameters-console") {
52999 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
53000 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
53001 consoleTag.style.height = consoleHeight;
53002 consoleTag.style.width = consoleWidth;
53004 CommandController.prototype.help = function () {
53007 .then(function (result) {
53008 console.log(result);
53011 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
53012 return CommandController;
53014 directives.CommandController = CommandController;
53015 })(directives = app.directives || (app.directives = {}));
53016 })(app || (app = {}));
53020 (function (directives) {
53021 var HeaderMenu = (function () {
53022 function HeaderMenu() {
53023 this.restrict = 'E';
53024 this.replace = true;
53025 this.templateUrl = 'templates/header-menu.html';
53026 this.controller = 'HeaderMenuController';
53027 this.controllerAs = 'hmc';
53030 HeaderMenu.Factory = function () {
53031 var directive = function () {
53032 return new HeaderMenu();
53038 directives.HeaderMenu = HeaderMenu;
53039 var HeaderMenuController = (function () {
53040 function HeaderMenuController($state) {
53041 this.$state = $state;
53042 this.isExecution = this.$state.current.name === 'execution';
53043 this.isWorkspace = this.$state.current.name === 'workspace';
53044 this.isHistory = this.$state.current.name === 'history';
53046 HeaderMenuController.prototype.transit = function (state) {
53047 this.$state.go(state);
53049 HeaderMenuController.$inject = ['$state'];
53050 return HeaderMenuController;
53052 directives.HeaderMenuController = HeaderMenuController;
53053 })(directives = app.directives || (app.directives = {}));
53054 })(app || (app = {}));
53058 (function (directives) {
53059 var Option = (function () {
53060 function Option() {
53061 this.restrict = 'E';
53062 this.replace = true;
53063 this.controller = 'optionController';
53064 this.bindToController = {
53069 this.templateUrl = 'templates/option.html';
53070 this.controllerAs = 'ctrl';
53072 Option.Factory = function () {
53073 var directive = function () {
53074 return new Option();
53076 directive.$inject = [];
53081 directives.Option = Option;
53082 var OptionController = (function () {
53083 function OptionController() {
53084 var controller = this;
53085 angular.forEach(controller.info.arg, function (arg) {
53086 if (arg.initialValue) {
53087 if (arg.formType === 'number') {
53088 arg.input = parseInt(arg.initialValue);
53091 arg.input = arg.initialValue;
53096 OptionController.$inject = [];
53097 return OptionController;
53099 directives.OptionController = OptionController;
53100 })(directives = app.directives || (app.directives = {}));
53101 })(app || (app = {}));
53105 (function (directives) {
53106 var Directory = (function () {
53107 function Directory() {
53108 this.restrict = 'E';
53109 this.replace = true;
53110 this.controller = 'directoryController';
53111 this.controllerAs = 'ctrl';
53112 this.bindToController = {
53118 this.templateUrl = 'templates/directory.html';
53120 Directory.Factory = function () {
53121 var directive = function () {
53122 return new Directory();
53128 directives.Directory = Directory;
53129 var DirectoryController = (function () {
53130 function DirectoryController(APIEndPoint, $scope) {
53131 this.APIEndPoint = APIEndPoint;
53132 this.$scope = $scope;
53133 var controller = this;
53135 .getFiles(this.info.fileId)
53137 .then(function (result) {
53138 if (result.status === 'success') {
53139 controller.files = result.info;
53140 angular.forEach(result.info, function (file) {
53141 if (file.fileType === '0') {
53143 if (controller.info.path === '/') {
53144 o.path = '/' + file.name;
53147 o.path = controller.info.path + '/' + file.name;
53149 controller.add()(o, controller.list);
53156 DirectoryController.$inject = ['APIEndPoint', '$scope'];
53157 return DirectoryController;
53159 directives.DirectoryController = DirectoryController;
53160 })(directives = app.directives || (app.directives = {}));
53161 })(app || (app = {}));
53166 (function (directives) {
53167 var Upload = (function () {
53168 function Upload() {
53169 this.restrict = 'E';
53170 this.replace = true;
53172 this.controller = 'UploadController';
53173 this.controllerAs = 'ctrl';
53174 this.bindToController = {
53180 this.templateUrl = 'templates/upload.html';
53181 console.log("templates/upload.html-constructor");
53183 Upload.Factory = function () {
53184 var directive = function () {
53185 return new Upload();
53187 directive.$inject = [];
53192 directives.Upload = Upload;
53193 var UploadController = (function () {
53194 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
53195 this.APIEndPoint = APIEndPoint;
53196 this.$scope = $scope;
53197 this.MyModal = MyModal;
53198 this.WebSocket = WebSocket;
53199 this.$window = $window;
53200 this.$rootScope = $rootScope;
53201 this.Console = Console;
53202 var controller = this;
53203 console.log("directive.upload-constructor");
53205 UploadController.prototype.submit = function () {
53206 console.log("submit: function not supported¥n");
53208 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
53209 return UploadController;
53211 directives.UploadController = UploadController;
53212 })(directives = app.directives || (app.directives = {}));
53213 })(app || (app = {}));
53217 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53219 (function (controllers) {
53220 var Execution = (function () {
53221 function Execution(MyModal, $scope) {
53222 this.MyModal = MyModal;
53223 this.$scope = $scope;
53224 this.commandInfoList = [];
53227 Execution.prototype.add = function () {
53228 this.$scope.$broadcast('close');
53229 var commandInfoList = this.commandInfoList;
53230 var commandInstance = this.MyModal.selectCommand();
53233 .then(function (command) {
53234 commandInfoList.push(new app.declares.CommandInfo(command));
53237 Execution.prototype.open = function () {
53238 var result = this.MyModal.open('SelectCommand');
53239 console.log(result);
53241 Execution.prototype.remove = function (index, list) {
53242 list.splice(index, 1);
53244 Execution.prototype.close = function () {
53245 console.log("close");
53247 Execution.$inject = ['MyModal', '$scope'];
53250 controllers.Execution = Execution;
53251 })(controllers = app.controllers || (app.controllers = {}));
53252 })(app || (app = {}));
53256 (function (controllers) {
53257 var Workspace = (function () {
53258 function Workspace($scope, APIEndPoint, MyModal) {
53259 this.$scope = $scope;
53260 this.APIEndPoint = APIEndPoint;
53261 this.MyModal = MyModal;
53262 this.directoryList = [];
53263 var controller = this;
53264 var directoryList = this.directoryList;
53266 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
53274 directoryList.push(o);
53276 Workspace.prototype.addDirectory = function (info, directoryList) {
53277 directoryList.push(info);
53280 Workspace.prototype.upload = function () {
53281 this.MyModal.upload();
53284 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53285 Workspace.prototype.debug = function () {
53286 this.MyModal.preview();
53288 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
53291 controllers.Workspace = Workspace;
53292 })(controllers = app.controllers || (app.controllers = {}));
53293 })(app || (app = {}));
53297 (function (controllers) {
53298 var History = (function () {
53299 function History($scope) {
53300 this.page = "History";
53302 History.$inject = ['$scope'];
53305 controllers.History = History;
53306 })(controllers = app.controllers || (app.controllers = {}));
53307 })(app || (app = {}));
53311 (function (controllers) {
53312 var SelectCommand = (function () {
53313 function SelectCommand($scope, APIEndPoint, $modalInstance) {
53314 this.APIEndPoint = APIEndPoint;
53315 this.$modalInstance = $modalInstance;
53316 var controller = this;
53319 .$promise.then(function (result) {
53320 controller.tags = result.info;
53324 .$promise.then(function (result) {
53325 controller.commands = result.info;
53327 this.currentTag = 'all';
53329 SelectCommand.prototype.changeTag = function (tag) {
53330 this.currentTag = tag;
53332 SelectCommand.prototype.selectCommand = function (command) {
53333 this.$modalInstance.close(command);
53335 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
53336 return SelectCommand;
53338 controllers.SelectCommand = SelectCommand;
53339 })(controllers = app.controllers || (app.controllers = {}));
53340 })(app || (app = {}));
53344 (function (controllers) {
53346 var Upload = (function () {
53347 function Upload($scope, APIEndPoint, $modalInstance) {
53348 this.APIEndPoint = APIEndPoint;
53349 this.$modalInstance = $modalInstance;
53350 var controller = this;
53351 console.log('controller.upload-controllers');
53353 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
53356 controllers.Upload = Upload;
53357 })(controllers = app.controllers || (app.controllers = {}));
53358 })(app || (app = {}));
53362 (function (controllers) {
53364 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53365 var Preview = (function () {
53366 function Preview($scope, APIEndPoint, $modalInstance) {
53367 this.APIEndPoint = APIEndPoint;
53368 this.$modalInstance = $modalInstance;
53369 var controller = this;
53370 console.log('preview');
53372 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
53375 controllers.Preview = Preview;
53376 })(controllers = app.controllers || (app.controllers = {}));
53377 })(app || (app = {}));
53379 (function (filters) {
53381 return function (commands, tag) {
53383 angular.forEach(commands, function (command) {
53385 angular.forEach(command.tags, function (value) {
53390 result.push(command);
53396 })(filters || (filters = {}));
53400 var appName = 'zephyr';
53401 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
53402 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
53403 $urlRouterProvider.otherwise('/execution');
53404 $locationProvider.html5Mode({
53409 .state('execution', {
53411 templateUrl: 'templates/execution.html',
53412 controller: 'executionController',
53415 .state('workspace', {
53417 templateUrl: 'templates/workspace.html',
53418 controller: 'workspaceController',
53421 .state('history', {
53423 templateUrl: 'templates/history.html',
53424 controller: 'historyController',
53428 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
53429 app.zephyr.service('MyModal', app.services.MyModal);
53430 app.zephyr.service('WebSocket', app.services.WebSocket);
53431 app.zephyr.service('Console', app.services.Console);
53432 app.zephyr.filter('Tag', filters.Tag);
53433 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
53434 app.zephyr.controller('previewController', app.controllers.Preview);
53436 app.zephyr.controller('uploadController', app.controllers.Upload);
53438 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53439 app.zephyr.controller('executionController', app.controllers.Execution);
53440 app.zephyr.controller('workspaceController', app.controllers.Workspace);
53441 app.zephyr.controller('historyController', app.controllers.History);
53442 app.zephyr.controller('commandController', app.directives.CommandController);
53443 app.zephyr.controller('optionController', app.directives.OptionController);
53444 app.zephyr.controller('directoryController', app.directives.DirectoryController);
53445 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
53447 app.zephyr.controller('uploadController', app.directives.UploadController);
53449 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53450 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
53451 app.zephyr.directive('command', app.directives.Command.Factory());
53452 app.zephyr.directive('option', app.directives.Option.Factory());
53453 app.zephyr.directive('directory', app.directives.Directory.Factory());
53454 })(app || (app = {}));
53462 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53463 /***/ function(module, exports) {
53468 (function (declares) {
53469 var CommandInfo = (function () {
53470 function CommandInfo(name) {
53473 return CommandInfo;
53475 declares.CommandInfo = CommandInfo;
53476 })(declares = app.declares || (app.declares = {}));
53477 })(app || (app = {}));
53481 (function (services) {
53482 var APIEndPoint = (function () {
53483 function APIEndPoint($resource, $http) {
53484 this.$resource = $resource;
53485 this.$http = $http;
53487 APIEndPoint.prototype.resource = function (endPoint, data) {
53488 var customAction = {
53494 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
53496 return this.$resource(endPoint, {}, { execute: execute });
53498 APIEndPoint.prototype.getOptionControlFile = function (command) {
53499 var endPoint = '/api/v1/optionControlFile/' + command;
53500 return this.resource(endPoint, {}).get();
53502 APIEndPoint.prototype.getFiles = function (fileId) {
53503 var endPoint = '/api/v1/workspace';
53505 endPoint += '/' + fileId;
53507 return this.resource(endPoint, {}).get();
53509 APIEndPoint.prototype.getDirectories = function () {
53510 var endPoint = '/api/v1/all/workspace/directory';
53511 return this.resource(endPoint, {}).get();
53513 APIEndPoint.prototype.getTags = function () {
53514 var endPoint = '/api/v1/tagList';
53515 return this.resource(endPoint, {}).get();
53517 APIEndPoint.prototype.getCommands = function () {
53518 var endPoint = '/api/v1/commandList';
53519 return this.resource(endPoint, {}).get();
53521 APIEndPoint.prototype.execute = function (data) {
53522 var endPoint = '/api/v1/execution';
53523 var fd = new FormData();
53524 fd.append('data', data);
53525 return this.$http.post(endPoint, fd, {
53526 headers: { 'Content-Type': undefined },
53527 transformRequest: angular.identity
53530 APIEndPoint.prototype.debug = function () {
53531 var endPoint = '/api/v1/debug';
53532 return this.$http.get(endPoint);
53535 APIEndPoint.prototype.upload = function () {
53536 var endPoint = '/api/v1/upload';
53537 return this.$http.get(endPoint);
53540 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53541 APIEndPoint.prototype.help = function (command) {
53542 var endPoint = '/api/v1/help/' + command;
53543 return this.$http.get(endPoint);
53545 return APIEndPoint;
53547 services.APIEndPoint = APIEndPoint;
53548 })(services = app.services || (app.services = {}));
53549 })(app || (app = {}));
53553 (function (services) {
53554 var MyModal = (function () {
53555 function MyModal($uibModal) {
53556 this.$uibModal = $uibModal;
53557 this.modalOption = {
53564 MyModal.prototype.open = function (modalName) {
53565 if (modalName === 'SelectCommand') {
53566 this.modalOption.templateUrl = 'templates/select-command.html';
53567 this.modalOption.size = 'lg';
53569 return this.$uibModal.open(this.modalOption);
53571 MyModal.prototype.selectCommand = function () {
53572 this.modalOption.templateUrl = 'templates/select-command.html';
53573 this.modalOption.controller = 'selectCommandController';
53574 this.modalOption.controllerAs = 'c';
53575 this.modalOption.size = 'lg';
53576 return this.$uibModal.open(this.modalOption);
53578 MyModal.prototype.preview = function () {
53579 this.modalOption.templateUrl = 'templates/preview.html';
53580 this.modalOption.controller = 'previewController';
53581 this.modalOption.controllerAs = 'c';
53582 this.modalOption.size = 'lg';
53583 return this.$uibModal.open(this.modalOption);
53586 MyModal.prototype.upload = function () {
53587 this.modalOption.templateUrl = 'templates/upload.html';
53588 this.modalOption.controller = 'uploadController';
53589 this.modalOption.controllerAs = 'c';
53590 this.modalOption.size = 'lg';
53591 return this.$uibModal.open(this.modalOption);
53594 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
53595 MyModal.$inject = ['$uibModal'];
53598 services.MyModal = MyModal;
53599 })(services = app.services || (app.services = {}));
53600 })(app || (app = {}));
53604 (function (services) {
53605 var WebSocket = (function () {
53606 function WebSocket($rootScope) {
53607 this.$rootScope = $rootScope;
53608 this.socket = io.connect();
53610 WebSocket.prototype.on = function (eventName, callback) {
53611 var socket = this.socket;
53612 var rootScope = this.$rootScope;
53613 socket.on(eventName, function () {
53614 var args = arguments;
53615 rootScope.$apply(function () {
53616 callback.apply(socket, args);
53620 WebSocket.prototype.emit = function (eventName, data, callback) {
53621 var socket = this.socket;
53622 var rootScope = this.$rootScope;
53623 this.socket.emit(eventName, data, function () {
53624 var args = arguments;
53625 rootScope.$apply(function () {
53627 callback.apply(socket, args);
53633 services.WebSocket = WebSocket;
53634 })(services = app.services || (app.services = {}));
53635 })(app || (app = {}));
53639 (function (services) {
53640 var Console = (function () {
53641 function Console(WebSocket, $rootScope) {
53642 this.WebSocket = WebSocket;
53643 this.$rootScope = $rootScope;
53644 this.WebSocket = WebSocket;
53645 this.$rootScope = $rootScope;
53646 this.directiveIDs = [];
53647 var directiveIDs = this.directiveIDs;
53648 this.WebSocket.on('console', function (d) {
53650 var message = d.message;
53651 if (directiveIDs.indexOf(id) > -1) {
53652 $rootScope.$emit(id, message);
53656 Console.prototype.addDirective = function (id) {
53657 if (!(this.directiveIDs.indexOf(id) > -1)) {
53658 this.directiveIDs.push(id);
53661 Console.prototype.removeDirective = function (id) {
53662 var i = this.directiveIDs.indexOf(id);
53664 this.directiveIDs.splice(i, 1);
53667 Console.prototype.showIDs = function () {
53668 console.log(this.directiveIDs);
53672 services.Console = Console;
53673 })(services = app.services || (app.services = {}));
53674 })(app || (app = {}));
53678 (function (directives) {
53679 var Command = (function () {
53680 function Command() {
53681 this.restrict = 'E';
53682 this.replace = true;
53684 this.controller = 'commandController';
53685 this.controllerAs = 'ctrl';
53686 this.bindToController = {
53692 this.templateUrl = 'templates/command.html';
53694 Command.Factory = function () {
53695 var directive = function () {
53696 return new Command();
53698 directive.$inject = [];
53703 directives.Command = Command;
53704 var CommandController = (function () {
53705 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
53706 this.APIEndPoint = APIEndPoint;
53707 this.$scope = $scope;
53708 this.MyModal = MyModal;
53709 this.WebSocket = WebSocket;
53710 this.$window = $window;
53711 this.$rootScope = $rootScope;
53712 this.Console = Console;
53713 var controller = this;
53715 .getOptionControlFile(this.name)
53717 .then(function (result) {
53718 controller.options = result.info;
53723 .then(function (result) {
53724 controller.dirs = result.info;
53726 this.heading = "[" + this.index + "]: dcdFilePrint";
53727 this.isOpen = true;
53728 this.$scope.$on('close', function () {
53729 controller.isOpen = false;
53733 return Math.floor((1 + Math.random()) * 0x10000)
53737 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
53738 s4() + '-' + s4() + s4() + s4();
53740 this.uuid = guid();
53741 this.Console.addDirective(this.uuid);
53742 this.Console.showIDs();
53744 CommandController.prototype.submit = function () {
53746 angular.forEach(this.options, function (option) {
53748 name: option.option,
53751 angular.forEach(option.arg, function (arg) {
53753 if (typeof arg.input === 'object') {
53754 obj.arguments.push(arg.input.name);
53757 obj.arguments.push(arg.input);
53761 if (obj.arguments.length > 0) {
53766 command: this.name,
53767 workspace: this.workspace.fileId,
53771 .execute(JSON.stringify(execObj))
53772 .then(function (result) {
53773 console.log(result);
53776 CommandController.prototype.removeMySelf = function (index) {
53777 this.$scope.$destroy();
53778 this.Console.removeDirective(this.uuid);
53779 this.remove()(index, this.list);
53780 this.Console.showIDs();
53782 CommandController.prototype.reloadFiles = function () {
53784 var fileId = this.workspace.fileId;
53788 .then(function (result) {
53789 var status = result.status;
53790 if (status === 'success') {
53791 _this.files = result.info;
53794 console.log(result.message);
53798 CommandController.prototype.debug = function () {
53799 var div = angular.element(this.$window.document).find("div");
53802 angular.forEach(div, function (v) {
53803 if (v.className === "panel-body console") {
53806 else if (v.className === "row parameters-console") {
53810 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
53811 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
53812 consoleTag.style.height = consoleHeight;
53813 consoleTag.style.width = consoleWidth;
53815 CommandController.prototype.help = function () {
53818 .then(function (result) {
53819 console.log(result);
53822 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
53823 return CommandController;
53825 directives.CommandController = CommandController;
53826 })(directives = app.directives || (app.directives = {}));
53827 })(app || (app = {}));
53831 (function (directives) {
53832 var HeaderMenu = (function () {
53833 function HeaderMenu() {
53834 this.restrict = 'E';
53835 this.replace = true;
53836 this.templateUrl = 'templates/header-menu.html';
53837 this.controller = 'HeaderMenuController';
53838 this.controllerAs = 'hmc';
53841 HeaderMenu.Factory = function () {
53842 var directive = function () {
53843 return new HeaderMenu();
53849 directives.HeaderMenu = HeaderMenu;
53850 var HeaderMenuController = (function () {
53851 function HeaderMenuController($state) {
53852 this.$state = $state;
53853 this.isExecution = this.$state.current.name === 'execution';
53854 this.isWorkspace = this.$state.current.name === 'workspace';
53855 this.isHistory = this.$state.current.name === 'history';
53857 HeaderMenuController.prototype.transit = function (state) {
53858 this.$state.go(state);
53860 HeaderMenuController.$inject = ['$state'];
53861 return HeaderMenuController;
53863 directives.HeaderMenuController = HeaderMenuController;
53864 })(directives = app.directives || (app.directives = {}));
53865 })(app || (app = {}));
53869 (function (directives) {
53870 var Option = (function () {
53871 function Option() {
53872 this.restrict = 'E';
53873 this.replace = true;
53874 this.controller = 'optionController';
53875 this.bindToController = {
53880 this.templateUrl = 'templates/option.html';
53881 this.controllerAs = 'ctrl';
53883 Option.Factory = function () {
53884 var directive = function () {
53885 return new Option();
53887 directive.$inject = [];
53892 directives.Option = Option;
53893 var OptionController = (function () {
53894 function OptionController() {
53895 var controller = this;
53896 angular.forEach(controller.info.arg, function (arg) {
53897 if (arg.initialValue) {
53898 if (arg.formType === 'number') {
53899 arg.input = parseInt(arg.initialValue);
53902 arg.input = arg.initialValue;
53907 OptionController.$inject = [];
53908 return OptionController;
53910 directives.OptionController = OptionController;
53911 })(directives = app.directives || (app.directives = {}));
53912 })(app || (app = {}));
53916 (function (directives) {
53917 var Directory = (function () {
53918 function Directory() {
53919 this.restrict = 'E';
53920 this.replace = true;
53921 this.controller = 'directoryController';
53922 this.controllerAs = 'ctrl';
53923 this.bindToController = {
53929 this.templateUrl = 'templates/directory.html';
53931 Directory.Factory = function () {
53932 var directive = function () {
53933 return new Directory();
53939 directives.Directory = Directory;
53940 var DirectoryController = (function () {
53941 function DirectoryController(APIEndPoint, $scope) {
53942 this.APIEndPoint = APIEndPoint;
53943 this.$scope = $scope;
53944 var controller = this;
53946 .getFiles(this.info.fileId)
53948 .then(function (result) {
53949 if (result.status === 'success') {
53950 controller.files = result.info;
53951 angular.forEach(result.info, function (file) {
53952 if (file.fileType === '0') {
53954 if (controller.info.path === '/') {
53955 o.path = '/' + file.name;
53958 o.path = controller.info.path + '/' + file.name;
53960 controller.add()(o, controller.list);
53967 DirectoryController.$inject = ['APIEndPoint', '$scope'];
53968 return DirectoryController;
53970 directives.DirectoryController = DirectoryController;
53971 })(directives = app.directives || (app.directives = {}));
53972 })(app || (app = {}));
53977 (function (directives) {
53978 var Upload = (function () {
53979 function Upload() {
53980 this.restrict = 'E';
53981 this.replace = true;
53983 this.controller = 'UploadController';
53984 this.controllerAs = 'ctrl';
53985 this.bindToController = {
53991 this.templateUrl = 'templates/upload.html';
53992 console.log("templates/upload.html-constructor");
53994 Upload.Factory = function () {
53995 var directive = function () {
53996 return new Upload();
53998 directive.$inject = [];
54003 directives.Upload = Upload;
54004 var UploadController = (function () {
54005 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
54006 this.APIEndPoint = APIEndPoint;
54007 this.$scope = $scope;
54008 this.MyModal = MyModal;
54009 this.WebSocket = WebSocket;
54010 this.$window = $window;
54011 this.$rootScope = $rootScope;
54012 this.Console = Console;
54013 var controller = this;
54014 console.log("directive.upload-constructor");
54016 UploadController.prototype.submit = function () {
54017 console.log("submit: function not supported¥n");
54019 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
54020 return UploadController;
54022 directives.UploadController = UploadController;
54023 })(directives = app.directives || (app.directives = {}));
54024 })(app || (app = {}));
54028 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54030 (function (controllers) {
54031 var Execution = (function () {
54032 function Execution(MyModal, $scope) {
54033 this.MyModal = MyModal;
54034 this.$scope = $scope;
54035 this.commandInfoList = [];
54038 Execution.prototype.add = function () {
54039 this.$scope.$broadcast('close');
54040 var commandInfoList = this.commandInfoList;
54041 var commandInstance = this.MyModal.selectCommand();
54044 .then(function (command) {
54045 commandInfoList.push(new app.declares.CommandInfo(command));
54048 Execution.prototype.open = function () {
54049 var result = this.MyModal.open('SelectCommand');
54050 console.log(result);
54052 Execution.prototype.remove = function (index, list) {
54053 list.splice(index, 1);
54055 Execution.prototype.close = function () {
54056 console.log("close");
54058 Execution.$inject = ['MyModal', '$scope'];
54061 controllers.Execution = Execution;
54062 })(controllers = app.controllers || (app.controllers = {}));
54063 })(app || (app = {}));
54067 (function (controllers) {
54068 var Workspace = (function () {
54069 function Workspace($scope, APIEndPoint, MyModal) {
54070 this.$scope = $scope;
54071 this.APIEndPoint = APIEndPoint;
54072 this.MyModal = MyModal;
54073 this.directoryList = [];
54074 var controller = this;
54075 var directoryList = this.directoryList;
54077 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
54085 directoryList.push(o);
54087 Workspace.prototype.addDirectory = function (info, directoryList) {
54088 directoryList.push(info);
54091 Workspace.prototype.upload = function () {
54092 this.MyModal.upload();
54095 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54096 Workspace.prototype.debug = function () {
54097 this.MyModal.preview();
54099 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
54102 controllers.Workspace = Workspace;
54103 })(controllers = app.controllers || (app.controllers = {}));
54104 })(app || (app = {}));
54108 (function (controllers) {
54109 var History = (function () {
54110 function History($scope) {
54111 this.page = "History";
54113 History.$inject = ['$scope'];
54116 controllers.History = History;
54117 })(controllers = app.controllers || (app.controllers = {}));
54118 })(app || (app = {}));
54122 (function (controllers) {
54123 var SelectCommand = (function () {
54124 function SelectCommand($scope, APIEndPoint, $modalInstance) {
54125 this.APIEndPoint = APIEndPoint;
54126 this.$modalInstance = $modalInstance;
54127 var controller = this;
54130 .$promise.then(function (result) {
54131 controller.tags = result.info;
54135 .$promise.then(function (result) {
54136 controller.commands = result.info;
54138 this.currentTag = 'all';
54140 SelectCommand.prototype.changeTag = function (tag) {
54141 this.currentTag = tag;
54143 SelectCommand.prototype.selectCommand = function (command) {
54144 this.$modalInstance.close(command);
54146 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54147 return SelectCommand;
54149 controllers.SelectCommand = SelectCommand;
54150 })(controllers = app.controllers || (app.controllers = {}));
54151 })(app || (app = {}));
54155 (function (controllers) {
54157 var Upload = (function () {
54158 function Upload($scope, APIEndPoint, $modalInstance) {
54159 this.APIEndPoint = APIEndPoint;
54160 this.$modalInstance = $modalInstance;
54161 var controller = this;
54162 console.log('controller.upload-controllers');
54164 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54167 controllers.Upload = Upload;
54168 })(controllers = app.controllers || (app.controllers = {}));
54169 })(app || (app = {}));
54173 (function (controllers) {
54175 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54176 var Preview = (function () {
54177 function Preview($scope, APIEndPoint, $modalInstance) {
54178 this.APIEndPoint = APIEndPoint;
54179 this.$modalInstance = $modalInstance;
54180 var controller = this;
54181 console.log('preview');
54183 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54186 controllers.Preview = Preview;
54187 })(controllers = app.controllers || (app.controllers = {}));
54188 })(app || (app = {}));
54190 (function (filters) {
54192 return function (commands, tag) {
54194 angular.forEach(commands, function (command) {
54196 angular.forEach(command.tags, function (value) {
54201 result.push(command);
54207 })(filters || (filters = {}));
54211 var appName = 'zephyr';
54212 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
54213 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
54214 $urlRouterProvider.otherwise('/execution');
54215 $locationProvider.html5Mode({
54220 .state('execution', {
54222 templateUrl: 'templates/execution.html',
54223 controller: 'executionController',
54226 .state('workspace', {
54228 templateUrl: 'templates/workspace.html',
54229 controller: 'workspaceController',
54232 .state('history', {
54234 templateUrl: 'templates/history.html',
54235 controller: 'historyController',
54239 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
54240 app.zephyr.service('MyModal', app.services.MyModal);
54241 app.zephyr.service('WebSocket', app.services.WebSocket);
54242 app.zephyr.service('Console', app.services.Console);
54243 app.zephyr.filter('Tag', filters.Tag);
54244 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
54245 app.zephyr.controller('previewController', app.controllers.Preview);
54247 app.zephyr.controller('uploadController', app.controllers.Upload);
54249 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54250 app.zephyr.controller('executionController', app.controllers.Execution);
54251 app.zephyr.controller('workspaceController', app.controllers.Workspace);
54252 app.zephyr.controller('historyController', app.controllers.History);
54253 app.zephyr.controller('commandController', app.directives.CommandController);
54254 app.zephyr.controller('optionController', app.directives.OptionController);
54255 app.zephyr.controller('directoryController', app.directives.DirectoryController);
54256 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
54258 app.zephyr.controller('uploadController', app.directives.UploadController);
54260 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54261 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
54262 app.zephyr.directive('command', app.directives.Command.Factory());
54263 app.zephyr.directive('option', app.directives.Option.Factory());
54264 app.zephyr.directive('directory', app.directives.Directory.Factory());
54265 })(app || (app = {}));
54273 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54274 /***/ function(module, exports) {
54279 (function (declares) {
54280 var CommandInfo = (function () {
54281 function CommandInfo(name) {
54284 return CommandInfo;
54286 declares.CommandInfo = CommandInfo;
54287 })(declares = app.declares || (app.declares = {}));
54288 })(app || (app = {}));
54292 (function (services) {
54293 var APIEndPoint = (function () {
54294 function APIEndPoint($resource, $http) {
54295 this.$resource = $resource;
54296 this.$http = $http;
54298 APIEndPoint.prototype.resource = function (endPoint, data) {
54299 var customAction = {
54305 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
54307 return this.$resource(endPoint, {}, { execute: execute });
54309 APIEndPoint.prototype.getOptionControlFile = function (command) {
54310 var endPoint = '/api/v1/optionControlFile/' + command;
54311 return this.resource(endPoint, {}).get();
54313 APIEndPoint.prototype.getFiles = function (fileId) {
54314 var endPoint = '/api/v1/workspace';
54316 endPoint += '/' + fileId;
54318 return this.resource(endPoint, {}).get();
54320 APIEndPoint.prototype.getDirectories = function () {
54321 var endPoint = '/api/v1/all/workspace/directory';
54322 return this.resource(endPoint, {}).get();
54324 APIEndPoint.prototype.getTags = function () {
54325 var endPoint = '/api/v1/tagList';
54326 return this.resource(endPoint, {}).get();
54328 APIEndPoint.prototype.getCommands = function () {
54329 var endPoint = '/api/v1/commandList';
54330 return this.resource(endPoint, {}).get();
54332 APIEndPoint.prototype.execute = function (data) {
54333 var endPoint = '/api/v1/execution';
54334 var fd = new FormData();
54335 fd.append('data', data);
54336 return this.$http.post(endPoint, fd, {
54337 headers: { 'Content-Type': undefined },
54338 transformRequest: angular.identity
54341 APIEndPoint.prototype.debug = function () {
54342 var endPoint = '/api/v1/debug';
54343 return this.$http.get(endPoint);
54346 APIEndPoint.prototype.upload = function () {
54347 var endPoint = '/api/v1/upload';
54348 return this.$http.get(endPoint);
54351 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54352 APIEndPoint.prototype.help = function (command) {
54353 var endPoint = '/api/v1/help/' + command;
54354 return this.$http.get(endPoint);
54356 return APIEndPoint;
54358 services.APIEndPoint = APIEndPoint;
54359 })(services = app.services || (app.services = {}));
54360 })(app || (app = {}));
54364 (function (services) {
54365 var MyModal = (function () {
54366 function MyModal($uibModal) {
54367 this.$uibModal = $uibModal;
54368 this.modalOption = {
54375 MyModal.prototype.open = function (modalName) {
54376 if (modalName === 'SelectCommand') {
54377 this.modalOption.templateUrl = 'templates/select-command.html';
54378 this.modalOption.size = 'lg';
54380 return this.$uibModal.open(this.modalOption);
54382 MyModal.prototype.selectCommand = function () {
54383 this.modalOption.templateUrl = 'templates/select-command.html';
54384 this.modalOption.controller = 'selectCommandController';
54385 this.modalOption.controllerAs = 'c';
54386 this.modalOption.size = 'lg';
54387 return this.$uibModal.open(this.modalOption);
54389 MyModal.prototype.preview = function () {
54390 this.modalOption.templateUrl = 'templates/preview.html';
54391 this.modalOption.controller = 'previewController';
54392 this.modalOption.controllerAs = 'c';
54393 this.modalOption.size = 'lg';
54394 return this.$uibModal.open(this.modalOption);
54397 MyModal.prototype.upload = function () {
54398 this.modalOption.templateUrl = 'templates/upload.html';
54399 this.modalOption.controller = 'uploadController';
54400 this.modalOption.controllerAs = 'c';
54401 this.modalOption.size = 'lg';
54402 return this.$uibModal.open(this.modalOption);
54405 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54406 MyModal.$inject = ['$uibModal'];
54409 services.MyModal = MyModal;
54410 })(services = app.services || (app.services = {}));
54411 })(app || (app = {}));
54415 (function (services) {
54416 var WebSocket = (function () {
54417 function WebSocket($rootScope) {
54418 this.$rootScope = $rootScope;
54419 this.socket = io.connect();
54421 WebSocket.prototype.on = function (eventName, callback) {
54422 var socket = this.socket;
54423 var rootScope = this.$rootScope;
54424 socket.on(eventName, function () {
54425 var args = arguments;
54426 rootScope.$apply(function () {
54427 callback.apply(socket, args);
54431 WebSocket.prototype.emit = function (eventName, data, callback) {
54432 var socket = this.socket;
54433 var rootScope = this.$rootScope;
54434 this.socket.emit(eventName, data, function () {
54435 var args = arguments;
54436 rootScope.$apply(function () {
54438 callback.apply(socket, args);
54444 services.WebSocket = WebSocket;
54445 })(services = app.services || (app.services = {}));
54446 })(app || (app = {}));
54450 (function (services) {
54451 var Console = (function () {
54452 function Console(WebSocket, $rootScope) {
54453 this.WebSocket = WebSocket;
54454 this.$rootScope = $rootScope;
54455 this.WebSocket = WebSocket;
54456 this.$rootScope = $rootScope;
54457 this.directiveIDs = [];
54458 var directiveIDs = this.directiveIDs;
54459 this.WebSocket.on('console', function (d) {
54461 var message = d.message;
54462 if (directiveIDs.indexOf(id) > -1) {
54463 $rootScope.$emit(id, message);
54467 Console.prototype.addDirective = function (id) {
54468 if (!(this.directiveIDs.indexOf(id) > -1)) {
54469 this.directiveIDs.push(id);
54472 Console.prototype.removeDirective = function (id) {
54473 var i = this.directiveIDs.indexOf(id);
54475 this.directiveIDs.splice(i, 1);
54478 Console.prototype.showIDs = function () {
54479 console.log(this.directiveIDs);
54483 services.Console = Console;
54484 })(services = app.services || (app.services = {}));
54485 })(app || (app = {}));
54489 (function (directives) {
54490 var Command = (function () {
54491 function Command() {
54492 this.restrict = 'E';
54493 this.replace = true;
54495 this.controller = 'commandController';
54496 this.controllerAs = 'ctrl';
54497 this.bindToController = {
54503 this.templateUrl = 'templates/command.html';
54505 Command.Factory = function () {
54506 var directive = function () {
54507 return new Command();
54509 directive.$inject = [];
54514 directives.Command = Command;
54515 var CommandController = (function () {
54516 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
54517 this.APIEndPoint = APIEndPoint;
54518 this.$scope = $scope;
54519 this.MyModal = MyModal;
54520 this.WebSocket = WebSocket;
54521 this.$window = $window;
54522 this.$rootScope = $rootScope;
54523 this.Console = Console;
54524 var controller = this;
54526 .getOptionControlFile(this.name)
54528 .then(function (result) {
54529 controller.options = result.info;
54534 .then(function (result) {
54535 controller.dirs = result.info;
54537 this.heading = "[" + this.index + "]: dcdFilePrint";
54538 this.isOpen = true;
54539 this.$scope.$on('close', function () {
54540 controller.isOpen = false;
54544 return Math.floor((1 + Math.random()) * 0x10000)
54548 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
54549 s4() + '-' + s4() + s4() + s4();
54551 this.uuid = guid();
54552 this.Console.addDirective(this.uuid);
54553 this.Console.showIDs();
54555 CommandController.prototype.submit = function () {
54557 angular.forEach(this.options, function (option) {
54559 name: option.option,
54562 angular.forEach(option.arg, function (arg) {
54564 if (typeof arg.input === 'object') {
54565 obj.arguments.push(arg.input.name);
54568 obj.arguments.push(arg.input);
54572 if (obj.arguments.length > 0) {
54577 command: this.name,
54578 workspace: this.workspace.fileId,
54582 .execute(JSON.stringify(execObj))
54583 .then(function (result) {
54584 console.log(result);
54587 CommandController.prototype.removeMySelf = function (index) {
54588 this.$scope.$destroy();
54589 this.Console.removeDirective(this.uuid);
54590 this.remove()(index, this.list);
54591 this.Console.showIDs();
54593 CommandController.prototype.reloadFiles = function () {
54595 var fileId = this.workspace.fileId;
54599 .then(function (result) {
54600 var status = result.status;
54601 if (status === 'success') {
54602 _this.files = result.info;
54605 console.log(result.message);
54609 CommandController.prototype.debug = function () {
54610 var div = angular.element(this.$window.document).find("div");
54613 angular.forEach(div, function (v) {
54614 if (v.className === "panel-body console") {
54617 else if (v.className === "row parameters-console") {
54621 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
54622 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
54623 consoleTag.style.height = consoleHeight;
54624 consoleTag.style.width = consoleWidth;
54626 CommandController.prototype.help = function () {
54629 .then(function (result) {
54630 console.log(result);
54633 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
54634 return CommandController;
54636 directives.CommandController = CommandController;
54637 })(directives = app.directives || (app.directives = {}));
54638 })(app || (app = {}));
54642 (function (directives) {
54643 var HeaderMenu = (function () {
54644 function HeaderMenu() {
54645 this.restrict = 'E';
54646 this.replace = true;
54647 this.templateUrl = 'templates/header-menu.html';
54648 this.controller = 'HeaderMenuController';
54649 this.controllerAs = 'hmc';
54652 HeaderMenu.Factory = function () {
54653 var directive = function () {
54654 return new HeaderMenu();
54660 directives.HeaderMenu = HeaderMenu;
54661 var HeaderMenuController = (function () {
54662 function HeaderMenuController($state) {
54663 this.$state = $state;
54664 this.isExecution = this.$state.current.name === 'execution';
54665 this.isWorkspace = this.$state.current.name === 'workspace';
54666 this.isHistory = this.$state.current.name === 'history';
54668 HeaderMenuController.prototype.transit = function (state) {
54669 this.$state.go(state);
54671 HeaderMenuController.$inject = ['$state'];
54672 return HeaderMenuController;
54674 directives.HeaderMenuController = HeaderMenuController;
54675 })(directives = app.directives || (app.directives = {}));
54676 })(app || (app = {}));
54680 (function (directives) {
54681 var Option = (function () {
54682 function Option() {
54683 this.restrict = 'E';
54684 this.replace = true;
54685 this.controller = 'optionController';
54686 this.bindToController = {
54691 this.templateUrl = 'templates/option.html';
54692 this.controllerAs = 'ctrl';
54694 Option.Factory = function () {
54695 var directive = function () {
54696 return new Option();
54698 directive.$inject = [];
54703 directives.Option = Option;
54704 var OptionController = (function () {
54705 function OptionController() {
54706 var controller = this;
54707 angular.forEach(controller.info.arg, function (arg) {
54708 if (arg.initialValue) {
54709 if (arg.formType === 'number') {
54710 arg.input = parseInt(arg.initialValue);
54713 arg.input = arg.initialValue;
54718 OptionController.$inject = [];
54719 return OptionController;
54721 directives.OptionController = OptionController;
54722 })(directives = app.directives || (app.directives = {}));
54723 })(app || (app = {}));
54727 (function (directives) {
54728 var Directory = (function () {
54729 function Directory() {
54730 this.restrict = 'E';
54731 this.replace = true;
54732 this.controller = 'directoryController';
54733 this.controllerAs = 'ctrl';
54734 this.bindToController = {
54740 this.templateUrl = 'templates/directory.html';
54742 Directory.Factory = function () {
54743 var directive = function () {
54744 return new Directory();
54750 directives.Directory = Directory;
54751 var DirectoryController = (function () {
54752 function DirectoryController(APIEndPoint, $scope) {
54753 this.APIEndPoint = APIEndPoint;
54754 this.$scope = $scope;
54755 var controller = this;
54757 .getFiles(this.info.fileId)
54759 .then(function (result) {
54760 if (result.status === 'success') {
54761 controller.files = result.info;
54762 angular.forEach(result.info, function (file) {
54763 if (file.fileType === '0') {
54765 if (controller.info.path === '/') {
54766 o.path = '/' + file.name;
54769 o.path = controller.info.path + '/' + file.name;
54771 controller.add()(o, controller.list);
54778 DirectoryController.$inject = ['APIEndPoint', '$scope'];
54779 return DirectoryController;
54781 directives.DirectoryController = DirectoryController;
54782 })(directives = app.directives || (app.directives = {}));
54783 })(app || (app = {}));
54788 (function (directives) {
54789 var Upload = (function () {
54790 function Upload() {
54791 this.restrict = 'E';
54792 this.replace = true;
54794 this.controller = 'UploadController';
54795 this.controllerAs = 'ctrl';
54796 this.bindToController = {
54802 this.templateUrl = 'templates/upload.html';
54803 console.log("templates/upload.html-constructor");
54805 Upload.Factory = function () {
54806 var directive = function () {
54807 return new Upload();
54809 directive.$inject = [];
54814 directives.Upload = Upload;
54815 var UploadController = (function () {
54816 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
54817 this.APIEndPoint = APIEndPoint;
54818 this.$scope = $scope;
54819 this.MyModal = MyModal;
54820 this.WebSocket = WebSocket;
54821 this.$window = $window;
54822 this.$rootScope = $rootScope;
54823 this.Console = Console;
54824 var controller = this;
54825 console.log("directive.upload-constructor");
54827 UploadController.prototype.submit = function () {
54828 console.log("submit: function not supported¥n");
54830 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
54831 return UploadController;
54833 directives.UploadController = UploadController;
54834 })(directives = app.directives || (app.directives = {}));
54835 })(app || (app = {}));
54839 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54841 (function (controllers) {
54842 var Execution = (function () {
54843 function Execution(MyModal, $scope) {
54844 this.MyModal = MyModal;
54845 this.$scope = $scope;
54846 this.commandInfoList = [];
54849 Execution.prototype.add = function () {
54850 this.$scope.$broadcast('close');
54851 var commandInfoList = this.commandInfoList;
54852 var commandInstance = this.MyModal.selectCommand();
54855 .then(function (command) {
54856 commandInfoList.push(new app.declares.CommandInfo(command));
54859 Execution.prototype.open = function () {
54860 var result = this.MyModal.open('SelectCommand');
54861 console.log(result);
54863 Execution.prototype.remove = function (index, list) {
54864 list.splice(index, 1);
54866 Execution.prototype.close = function () {
54867 console.log("close");
54869 Execution.$inject = ['MyModal', '$scope'];
54872 controllers.Execution = Execution;
54873 })(controllers = app.controllers || (app.controllers = {}));
54874 })(app || (app = {}));
54878 (function (controllers) {
54879 var Workspace = (function () {
54880 function Workspace($scope, APIEndPoint, MyModal) {
54881 this.$scope = $scope;
54882 this.APIEndPoint = APIEndPoint;
54883 this.MyModal = MyModal;
54884 this.directoryList = [];
54885 var controller = this;
54886 var directoryList = this.directoryList;
54888 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
54896 directoryList.push(o);
54898 Workspace.prototype.addDirectory = function (info, directoryList) {
54899 directoryList.push(info);
54902 Workspace.prototype.upload = function () {
54903 this.MyModal.upload();
54906 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54907 Workspace.prototype.debug = function () {
54908 this.MyModal.preview();
54910 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
54913 controllers.Workspace = Workspace;
54914 })(controllers = app.controllers || (app.controllers = {}));
54915 })(app || (app = {}));
54919 (function (controllers) {
54920 var History = (function () {
54921 function History($scope) {
54922 this.page = "History";
54924 History.$inject = ['$scope'];
54927 controllers.History = History;
54928 })(controllers = app.controllers || (app.controllers = {}));
54929 })(app || (app = {}));
54933 (function (controllers) {
54934 var SelectCommand = (function () {
54935 function SelectCommand($scope, APIEndPoint, $modalInstance) {
54936 this.APIEndPoint = APIEndPoint;
54937 this.$modalInstance = $modalInstance;
54938 var controller = this;
54941 .$promise.then(function (result) {
54942 controller.tags = result.info;
54946 .$promise.then(function (result) {
54947 controller.commands = result.info;
54949 this.currentTag = 'all';
54951 SelectCommand.prototype.changeTag = function (tag) {
54952 this.currentTag = tag;
54954 SelectCommand.prototype.selectCommand = function (command) {
54955 this.$modalInstance.close(command);
54957 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54958 return SelectCommand;
54960 controllers.SelectCommand = SelectCommand;
54961 })(controllers = app.controllers || (app.controllers = {}));
54962 })(app || (app = {}));
54966 (function (controllers) {
54968 var Upload = (function () {
54969 function Upload($scope, APIEndPoint, $modalInstance) {
54970 this.APIEndPoint = APIEndPoint;
54971 this.$modalInstance = $modalInstance;
54972 var controller = this;
54973 console.log('controller.upload-controllers');
54975 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54978 controllers.Upload = Upload;
54979 })(controllers = app.controllers || (app.controllers = {}));
54980 })(app || (app = {}));
54984 (function (controllers) {
54986 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
54987 var Preview = (function () {
54988 function Preview($scope, APIEndPoint, $modalInstance) {
54989 this.APIEndPoint = APIEndPoint;
54990 this.$modalInstance = $modalInstance;
54991 var controller = this;
54992 console.log('preview');
54994 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
54997 controllers.Preview = Preview;
54998 })(controllers = app.controllers || (app.controllers = {}));
54999 })(app || (app = {}));
55001 (function (filters) {
55003 return function (commands, tag) {
55005 angular.forEach(commands, function (command) {
55007 angular.forEach(command.tags, function (value) {
55012 result.push(command);
55018 })(filters || (filters = {}));
55022 var appName = 'zephyr';
55023 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
55024 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
55025 $urlRouterProvider.otherwise('/execution');
55026 $locationProvider.html5Mode({
55031 .state('execution', {
55033 templateUrl: 'templates/execution.html',
55034 controller: 'executionController',
55037 .state('workspace', {
55039 templateUrl: 'templates/workspace.html',
55040 controller: 'workspaceController',
55043 .state('history', {
55045 templateUrl: 'templates/history.html',
55046 controller: 'historyController',
55050 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
55051 app.zephyr.service('MyModal', app.services.MyModal);
55052 app.zephyr.service('WebSocket', app.services.WebSocket);
55053 app.zephyr.service('Console', app.services.Console);
55054 app.zephyr.filter('Tag', filters.Tag);
55055 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
55056 app.zephyr.controller('previewController', app.controllers.Preview);
55058 app.zephyr.controller('uploadController', app.controllers.Upload);
55060 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55061 app.zephyr.controller('executionController', app.controllers.Execution);
55062 app.zephyr.controller('workspaceController', app.controllers.Workspace);
55063 app.zephyr.controller('historyController', app.controllers.History);
55064 app.zephyr.controller('commandController', app.directives.CommandController);
55065 app.zephyr.controller('optionController', app.directives.OptionController);
55066 app.zephyr.controller('directoryController', app.directives.DirectoryController);
55067 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
55069 app.zephyr.controller('uploadController', app.directives.UploadController);
55071 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55072 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
55073 app.zephyr.directive('command', app.directives.Command.Factory());
55074 app.zephyr.directive('option', app.directives.Option.Factory());
55075 app.zephyr.directive('directory', app.directives.Directory.Factory());
55076 })(app || (app = {}));
55084 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55085 /***/ function(module, exports) {
55090 (function (declares) {
55091 var CommandInfo = (function () {
55092 function CommandInfo(name) {
55095 return CommandInfo;
55097 declares.CommandInfo = CommandInfo;
55098 })(declares = app.declares || (app.declares = {}));
55099 })(app || (app = {}));
55103 (function (services) {
55104 var APIEndPoint = (function () {
55105 function APIEndPoint($resource, $http) {
55106 this.$resource = $resource;
55107 this.$http = $http;
55109 APIEndPoint.prototype.resource = function (endPoint, data) {
55110 var customAction = {
55116 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
55118 return this.$resource(endPoint, {}, { execute: execute });
55120 APIEndPoint.prototype.getOptionControlFile = function (command) {
55121 var endPoint = '/api/v1/optionControlFile/' + command;
55122 return this.resource(endPoint, {}).get();
55124 APIEndPoint.prototype.getFiles = function (fileId) {
55125 var endPoint = '/api/v1/workspace';
55127 endPoint += '/' + fileId;
55129 return this.resource(endPoint, {}).get();
55131 APIEndPoint.prototype.getDirectories = function () {
55132 var endPoint = '/api/v1/all/workspace/directory';
55133 return this.resource(endPoint, {}).get();
55135 APIEndPoint.prototype.getTags = function () {
55136 var endPoint = '/api/v1/tagList';
55137 return this.resource(endPoint, {}).get();
55139 APIEndPoint.prototype.getCommands = function () {
55140 var endPoint = '/api/v1/commandList';
55141 return this.resource(endPoint, {}).get();
55143 APIEndPoint.prototype.execute = function (data) {
55144 var endPoint = '/api/v1/execution';
55145 var fd = new FormData();
55146 fd.append('data', data);
55147 return this.$http.post(endPoint, fd, {
55148 headers: { 'Content-Type': undefined },
55149 transformRequest: angular.identity
55152 APIEndPoint.prototype.debug = function () {
55153 var endPoint = '/api/v1/debug';
55154 return this.$http.get(endPoint);
55157 APIEndPoint.prototype.upload = function () {
55158 var endPoint = '/api/v1/upload';
55159 return this.$http.get(endPoint);
55162 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55163 APIEndPoint.prototype.help = function (command) {
55164 var endPoint = '/api/v1/help/' + command;
55165 return this.$http.get(endPoint);
55167 return APIEndPoint;
55169 services.APIEndPoint = APIEndPoint;
55170 })(services = app.services || (app.services = {}));
55171 })(app || (app = {}));
55175 (function (services) {
55176 var MyModal = (function () {
55177 function MyModal($uibModal) {
55178 this.$uibModal = $uibModal;
55179 this.modalOption = {
55186 MyModal.prototype.open = function (modalName) {
55187 if (modalName === 'SelectCommand') {
55188 this.modalOption.templateUrl = 'templates/select-command.html';
55189 this.modalOption.size = 'lg';
55191 return this.$uibModal.open(this.modalOption);
55193 MyModal.prototype.selectCommand = function () {
55194 this.modalOption.templateUrl = 'templates/select-command.html';
55195 this.modalOption.controller = 'selectCommandController';
55196 this.modalOption.controllerAs = 'c';
55197 this.modalOption.size = 'lg';
55198 return this.$uibModal.open(this.modalOption);
55200 MyModal.prototype.preview = function () {
55201 this.modalOption.templateUrl = 'templates/preview.html';
55202 this.modalOption.controller = 'previewController';
55203 this.modalOption.controllerAs = 'c';
55204 this.modalOption.size = 'lg';
55205 return this.$uibModal.open(this.modalOption);
55208 MyModal.prototype.upload = function () {
55209 this.modalOption.templateUrl = 'templates/upload.html';
55210 this.modalOption.controller = 'uploadController';
55211 this.modalOption.controllerAs = 'c';
55212 this.modalOption.size = 'lg';
55213 return this.$uibModal.open(this.modalOption);
55216 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55217 MyModal.$inject = ['$uibModal'];
55220 services.MyModal = MyModal;
55221 })(services = app.services || (app.services = {}));
55222 })(app || (app = {}));
55226 (function (services) {
55227 var WebSocket = (function () {
55228 function WebSocket($rootScope) {
55229 this.$rootScope = $rootScope;
55230 this.socket = io.connect();
55232 WebSocket.prototype.on = function (eventName, callback) {
55233 var socket = this.socket;
55234 var rootScope = this.$rootScope;
55235 socket.on(eventName, function () {
55236 var args = arguments;
55237 rootScope.$apply(function () {
55238 callback.apply(socket, args);
55242 WebSocket.prototype.emit = function (eventName, data, callback) {
55243 var socket = this.socket;
55244 var rootScope = this.$rootScope;
55245 this.socket.emit(eventName, data, function () {
55246 var args = arguments;
55247 rootScope.$apply(function () {
55249 callback.apply(socket, args);
55255 services.WebSocket = WebSocket;
55256 })(services = app.services || (app.services = {}));
55257 })(app || (app = {}));
55261 (function (services) {
55262 var Console = (function () {
55263 function Console(WebSocket, $rootScope) {
55264 this.WebSocket = WebSocket;
55265 this.$rootScope = $rootScope;
55266 this.WebSocket = WebSocket;
55267 this.$rootScope = $rootScope;
55268 this.directiveIDs = [];
55269 var directiveIDs = this.directiveIDs;
55270 this.WebSocket.on('console', function (d) {
55272 var message = d.message;
55273 if (directiveIDs.indexOf(id) > -1) {
55274 $rootScope.$emit(id, message);
55278 Console.prototype.addDirective = function (id) {
55279 if (!(this.directiveIDs.indexOf(id) > -1)) {
55280 this.directiveIDs.push(id);
55283 Console.prototype.removeDirective = function (id) {
55284 var i = this.directiveIDs.indexOf(id);
55286 this.directiveIDs.splice(i, 1);
55289 Console.prototype.showIDs = function () {
55290 console.log(this.directiveIDs);
55294 services.Console = Console;
55295 })(services = app.services || (app.services = {}));
55296 })(app || (app = {}));
55300 (function (directives) {
55301 var Command = (function () {
55302 function Command() {
55303 this.restrict = 'E';
55304 this.replace = true;
55306 this.controller = 'commandController';
55307 this.controllerAs = 'ctrl';
55308 this.bindToController = {
55314 this.templateUrl = 'templates/command.html';
55316 Command.Factory = function () {
55317 var directive = function () {
55318 return new Command();
55320 directive.$inject = [];
55325 directives.Command = Command;
55326 var CommandController = (function () {
55327 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
55328 this.APIEndPoint = APIEndPoint;
55329 this.$scope = $scope;
55330 this.MyModal = MyModal;
55331 this.WebSocket = WebSocket;
55332 this.$window = $window;
55333 this.$rootScope = $rootScope;
55334 this.Console = Console;
55335 var controller = this;
55337 .getOptionControlFile(this.name)
55339 .then(function (result) {
55340 controller.options = result.info;
55345 .then(function (result) {
55346 controller.dirs = result.info;
55348 this.heading = "[" + this.index + "]: dcdFilePrint";
55349 this.isOpen = true;
55350 this.$scope.$on('close', function () {
55351 controller.isOpen = false;
55355 return Math.floor((1 + Math.random()) * 0x10000)
55359 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
55360 s4() + '-' + s4() + s4() + s4();
55362 this.uuid = guid();
55363 this.Console.addDirective(this.uuid);
55364 this.Console.showIDs();
55366 CommandController.prototype.submit = function () {
55368 angular.forEach(this.options, function (option) {
55370 name: option.option,
55373 angular.forEach(option.arg, function (arg) {
55375 if (typeof arg.input === 'object') {
55376 obj.arguments.push(arg.input.name);
55379 obj.arguments.push(arg.input);
55383 if (obj.arguments.length > 0) {
55388 command: this.name,
55389 workspace: this.workspace.fileId,
55393 .execute(JSON.stringify(execObj))
55394 .then(function (result) {
55395 console.log(result);
55398 CommandController.prototype.removeMySelf = function (index) {
55399 this.$scope.$destroy();
55400 this.Console.removeDirective(this.uuid);
55401 this.remove()(index, this.list);
55402 this.Console.showIDs();
55404 CommandController.prototype.reloadFiles = function () {
55406 var fileId = this.workspace.fileId;
55410 .then(function (result) {
55411 var status = result.status;
55412 if (status === 'success') {
55413 _this.files = result.info;
55416 console.log(result.message);
55420 CommandController.prototype.debug = function () {
55421 var div = angular.element(this.$window.document).find("div");
55424 angular.forEach(div, function (v) {
55425 if (v.className === "panel-body console") {
55428 else if (v.className === "row parameters-console") {
55432 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
55433 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
55434 consoleTag.style.height = consoleHeight;
55435 consoleTag.style.width = consoleWidth;
55437 CommandController.prototype.help = function () {
55440 .then(function (result) {
55441 console.log(result);
55444 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
55445 return CommandController;
55447 directives.CommandController = CommandController;
55448 })(directives = app.directives || (app.directives = {}));
55449 })(app || (app = {}));
55453 (function (directives) {
55454 var HeaderMenu = (function () {
55455 function HeaderMenu() {
55456 this.restrict = 'E';
55457 this.replace = true;
55458 this.templateUrl = 'templates/header-menu.html';
55459 this.controller = 'HeaderMenuController';
55460 this.controllerAs = 'hmc';
55463 HeaderMenu.Factory = function () {
55464 var directive = function () {
55465 return new HeaderMenu();
55471 directives.HeaderMenu = HeaderMenu;
55472 var HeaderMenuController = (function () {
55473 function HeaderMenuController($state) {
55474 this.$state = $state;
55475 this.isExecution = this.$state.current.name === 'execution';
55476 this.isWorkspace = this.$state.current.name === 'workspace';
55477 this.isHistory = this.$state.current.name === 'history';
55479 HeaderMenuController.prototype.transit = function (state) {
55480 this.$state.go(state);
55482 HeaderMenuController.$inject = ['$state'];
55483 return HeaderMenuController;
55485 directives.HeaderMenuController = HeaderMenuController;
55486 })(directives = app.directives || (app.directives = {}));
55487 })(app || (app = {}));
55491 (function (directives) {
55492 var Option = (function () {
55493 function Option() {
55494 this.restrict = 'E';
55495 this.replace = true;
55496 this.controller = 'optionController';
55497 this.bindToController = {
55502 this.templateUrl = 'templates/option.html';
55503 this.controllerAs = 'ctrl';
55505 Option.Factory = function () {
55506 var directive = function () {
55507 return new Option();
55509 directive.$inject = [];
55514 directives.Option = Option;
55515 var OptionController = (function () {
55516 function OptionController() {
55517 var controller = this;
55518 angular.forEach(controller.info.arg, function (arg) {
55519 if (arg.initialValue) {
55520 if (arg.formType === 'number') {
55521 arg.input = parseInt(arg.initialValue);
55524 arg.input = arg.initialValue;
55529 OptionController.$inject = [];
55530 return OptionController;
55532 directives.OptionController = OptionController;
55533 })(directives = app.directives || (app.directives = {}));
55534 })(app || (app = {}));
55538 (function (directives) {
55539 var Directory = (function () {
55540 function Directory() {
55541 this.restrict = 'E';
55542 this.replace = true;
55543 this.controller = 'directoryController';
55544 this.controllerAs = 'ctrl';
55545 this.bindToController = {
55551 this.templateUrl = 'templates/directory.html';
55553 Directory.Factory = function () {
55554 var directive = function () {
55555 return new Directory();
55561 directives.Directory = Directory;
55562 var DirectoryController = (function () {
55563 function DirectoryController(APIEndPoint, $scope) {
55564 this.APIEndPoint = APIEndPoint;
55565 this.$scope = $scope;
55566 var controller = this;
55568 .getFiles(this.info.fileId)
55570 .then(function (result) {
55571 if (result.status === 'success') {
55572 controller.files = result.info;
55573 angular.forEach(result.info, function (file) {
55574 if (file.fileType === '0') {
55576 if (controller.info.path === '/') {
55577 o.path = '/' + file.name;
55580 o.path = controller.info.path + '/' + file.name;
55582 controller.add()(o, controller.list);
55589 DirectoryController.$inject = ['APIEndPoint', '$scope'];
55590 return DirectoryController;
55592 directives.DirectoryController = DirectoryController;
55593 })(directives = app.directives || (app.directives = {}));
55594 })(app || (app = {}));
55599 (function (directives) {
55600 var Upload = (function () {
55601 function Upload() {
55602 this.restrict = 'E';
55603 this.replace = true;
55605 this.controller = 'UploadController';
55606 this.controllerAs = 'ctrl';
55607 this.bindToController = {
55613 this.templateUrl = 'templates/upload.html';
55614 console.log("templates/upload.html-constructor");
55616 Upload.Factory = function () {
55617 var directive = function () {
55618 return new Upload();
55620 directive.$inject = [];
55625 directives.Upload = Upload;
55626 var UploadController = (function () {
55627 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
55628 this.APIEndPoint = APIEndPoint;
55629 this.$scope = $scope;
55630 this.MyModal = MyModal;
55631 this.WebSocket = WebSocket;
55632 this.$window = $window;
55633 this.$rootScope = $rootScope;
55634 this.Console = Console;
55635 var controller = this;
55636 console.log("directive.upload-constructor");
55638 UploadController.prototype.submit = function () {
55639 console.log("submit: function not supported¥n");
55641 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
55642 return UploadController;
55644 directives.UploadController = UploadController;
55645 })(directives = app.directives || (app.directives = {}));
55646 })(app || (app = {}));
55650 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55652 (function (controllers) {
55653 var Execution = (function () {
55654 function Execution(MyModal, $scope) {
55655 this.MyModal = MyModal;
55656 this.$scope = $scope;
55657 this.commandInfoList = [];
55660 Execution.prototype.add = function () {
55661 this.$scope.$broadcast('close');
55662 var commandInfoList = this.commandInfoList;
55663 var commandInstance = this.MyModal.selectCommand();
55666 .then(function (command) {
55667 commandInfoList.push(new app.declares.CommandInfo(command));
55670 Execution.prototype.open = function () {
55671 var result = this.MyModal.open('SelectCommand');
55672 console.log(result);
55674 Execution.prototype.remove = function (index, list) {
55675 list.splice(index, 1);
55677 Execution.prototype.close = function () {
55678 console.log("close");
55680 Execution.$inject = ['MyModal', '$scope'];
55683 controllers.Execution = Execution;
55684 })(controllers = app.controllers || (app.controllers = {}));
55685 })(app || (app = {}));
55689 (function (controllers) {
55690 var Workspace = (function () {
55691 function Workspace($scope, APIEndPoint, MyModal) {
55692 this.$scope = $scope;
55693 this.APIEndPoint = APIEndPoint;
55694 this.MyModal = MyModal;
55695 this.directoryList = [];
55696 var controller = this;
55697 var directoryList = this.directoryList;
55699 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
55707 directoryList.push(o);
55709 Workspace.prototype.addDirectory = function (info, directoryList) {
55710 directoryList.push(info);
55713 Workspace.prototype.upload = function () {
55714 this.MyModal.upload();
55717 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55718 Workspace.prototype.debug = function () {
55719 this.MyModal.preview();
55721 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
55724 controllers.Workspace = Workspace;
55725 })(controllers = app.controllers || (app.controllers = {}));
55726 })(app || (app = {}));
55730 (function (controllers) {
55731 var History = (function () {
55732 function History($scope) {
55733 this.page = "History";
55735 History.$inject = ['$scope'];
55738 controllers.History = History;
55739 })(controllers = app.controllers || (app.controllers = {}));
55740 })(app || (app = {}));
55744 (function (controllers) {
55745 var SelectCommand = (function () {
55746 function SelectCommand($scope, APIEndPoint, $modalInstance) {
55747 this.APIEndPoint = APIEndPoint;
55748 this.$modalInstance = $modalInstance;
55749 var controller = this;
55752 .$promise.then(function (result) {
55753 controller.tags = result.info;
55757 .$promise.then(function (result) {
55758 controller.commands = result.info;
55760 this.currentTag = 'all';
55762 SelectCommand.prototype.changeTag = function (tag) {
55763 this.currentTag = tag;
55765 SelectCommand.prototype.selectCommand = function (command) {
55766 this.$modalInstance.close(command);
55768 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
55769 return SelectCommand;
55771 controllers.SelectCommand = SelectCommand;
55772 })(controllers = app.controllers || (app.controllers = {}));
55773 })(app || (app = {}));
55777 (function (controllers) {
55779 var Upload = (function () {
55780 function Upload($scope, APIEndPoint, $modalInstance) {
55781 this.APIEndPoint = APIEndPoint;
55782 this.$modalInstance = $modalInstance;
55783 var controller = this;
55784 console.log('controller.upload-controllers');
55786 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
55789 controllers.Upload = Upload;
55790 })(controllers = app.controllers || (app.controllers = {}));
55791 })(app || (app = {}));
55795 (function (controllers) {
55797 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55798 var Preview = (function () {
55799 function Preview($scope, APIEndPoint, $modalInstance) {
55800 this.APIEndPoint = APIEndPoint;
55801 this.$modalInstance = $modalInstance;
55802 var controller = this;
55803 console.log('preview');
55805 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
55808 controllers.Preview = Preview;
55809 })(controllers = app.controllers || (app.controllers = {}));
55810 })(app || (app = {}));
55812 (function (filters) {
55814 return function (commands, tag) {
55816 angular.forEach(commands, function (command) {
55818 angular.forEach(command.tags, function (value) {
55823 result.push(command);
55829 })(filters || (filters = {}));
55833 var appName = 'zephyr';
55834 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
55835 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
55836 $urlRouterProvider.otherwise('/execution');
55837 $locationProvider.html5Mode({
55842 .state('execution', {
55844 templateUrl: 'templates/execution.html',
55845 controller: 'executionController',
55848 .state('workspace', {
55850 templateUrl: 'templates/workspace.html',
55851 controller: 'workspaceController',
55854 .state('history', {
55856 templateUrl: 'templates/history.html',
55857 controller: 'historyController',
55861 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
55862 app.zephyr.service('MyModal', app.services.MyModal);
55863 app.zephyr.service('WebSocket', app.services.WebSocket);
55864 app.zephyr.service('Console', app.services.Console);
55865 app.zephyr.filter('Tag', filters.Tag);
55866 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
55867 app.zephyr.controller('previewController', app.controllers.Preview);
55869 app.zephyr.controller('uploadController', app.controllers.Upload);
55871 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55872 app.zephyr.controller('executionController', app.controllers.Execution);
55873 app.zephyr.controller('workspaceController', app.controllers.Workspace);
55874 app.zephyr.controller('historyController', app.controllers.History);
55875 app.zephyr.controller('commandController', app.directives.CommandController);
55876 app.zephyr.controller('optionController', app.directives.OptionController);
55877 app.zephyr.controller('directoryController', app.directives.DirectoryController);
55878 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
55880 app.zephyr.controller('uploadController', app.directives.UploadController);
55882 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55883 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
55884 app.zephyr.directive('command', app.directives.Command.Factory());
55885 app.zephyr.directive('option', app.directives.Option.Factory());
55886 app.zephyr.directive('directory', app.directives.Directory.Factory());
55887 })(app || (app = {}));
55895 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55896 /***/ function(module, exports) {
55901 (function (declares) {
55902 var CommandInfo = (function () {
55903 function CommandInfo(name) {
55906 return CommandInfo;
55908 declares.CommandInfo = CommandInfo;
55909 })(declares = app.declares || (app.declares = {}));
55910 })(app || (app = {}));
55914 (function (services) {
55915 var APIEndPoint = (function () {
55916 function APIEndPoint($resource, $http) {
55917 this.$resource = $resource;
55918 this.$http = $http;
55920 APIEndPoint.prototype.resource = function (endPoint, data) {
55921 var customAction = {
55927 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
55929 return this.$resource(endPoint, {}, { execute: execute });
55931 APIEndPoint.prototype.getOptionControlFile = function (command) {
55932 var endPoint = '/api/v1/optionControlFile/' + command;
55933 return this.resource(endPoint, {}).get();
55935 APIEndPoint.prototype.getFiles = function (fileId) {
55936 var endPoint = '/api/v1/workspace';
55938 endPoint += '/' + fileId;
55940 return this.resource(endPoint, {}).get();
55942 APIEndPoint.prototype.getDirectories = function () {
55943 var endPoint = '/api/v1/all/workspace/directory';
55944 return this.resource(endPoint, {}).get();
55946 APIEndPoint.prototype.getTags = function () {
55947 var endPoint = '/api/v1/tagList';
55948 return this.resource(endPoint, {}).get();
55950 APIEndPoint.prototype.getCommands = function () {
55951 var endPoint = '/api/v1/commandList';
55952 return this.resource(endPoint, {}).get();
55954 APIEndPoint.prototype.execute = function (data) {
55955 var endPoint = '/api/v1/execution';
55956 var fd = new FormData();
55957 fd.append('data', data);
55958 return this.$http.post(endPoint, fd, {
55959 headers: { 'Content-Type': undefined },
55960 transformRequest: angular.identity
55963 APIEndPoint.prototype.debug = function () {
55964 var endPoint = '/api/v1/debug';
55965 return this.$http.get(endPoint);
55968 APIEndPoint.prototype.upload = function () {
55969 var endPoint = '/api/v1/upload';
55970 return this.$http.get(endPoint);
55973 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
55974 APIEndPoint.prototype.help = function (command) {
55975 var endPoint = '/api/v1/help/' + command;
55976 return this.$http.get(endPoint);
55978 return APIEndPoint;
55980 services.APIEndPoint = APIEndPoint;
55981 })(services = app.services || (app.services = {}));
55982 })(app || (app = {}));
55986 (function (services) {
55987 var MyModal = (function () {
55988 function MyModal($uibModal) {
55989 this.$uibModal = $uibModal;
55990 this.modalOption = {
55997 MyModal.prototype.open = function (modalName) {
55998 if (modalName === 'SelectCommand') {
55999 this.modalOption.templateUrl = 'templates/select-command.html';
56000 this.modalOption.size = 'lg';
56002 return this.$uibModal.open(this.modalOption);
56004 MyModal.prototype.selectCommand = function () {
56005 this.modalOption.templateUrl = 'templates/select-command.html';
56006 this.modalOption.controller = 'selectCommandController';
56007 this.modalOption.controllerAs = 'c';
56008 this.modalOption.size = 'lg';
56009 return this.$uibModal.open(this.modalOption);
56011 MyModal.prototype.preview = function () {
56012 this.modalOption.templateUrl = 'templates/preview.html';
56013 this.modalOption.controller = 'previewController';
56014 this.modalOption.controllerAs = 'c';
56015 this.modalOption.size = 'lg';
56016 return this.$uibModal.open(this.modalOption);
56019 MyModal.prototype.upload = function () {
56020 this.modalOption.templateUrl = 'templates/upload.html';
56021 this.modalOption.controller = 'uploadController';
56022 this.modalOption.controllerAs = 'c';
56023 this.modalOption.size = 'lg';
56024 return this.$uibModal.open(this.modalOption);
56027 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56028 MyModal.$inject = ['$uibModal'];
56031 services.MyModal = MyModal;
56032 })(services = app.services || (app.services = {}));
56033 })(app || (app = {}));
56037 (function (services) {
56038 var WebSocket = (function () {
56039 function WebSocket($rootScope) {
56040 this.$rootScope = $rootScope;
56041 this.socket = io.connect();
56043 WebSocket.prototype.on = function (eventName, callback) {
56044 var socket = this.socket;
56045 var rootScope = this.$rootScope;
56046 socket.on(eventName, function () {
56047 var args = arguments;
56048 rootScope.$apply(function () {
56049 callback.apply(socket, args);
56053 WebSocket.prototype.emit = function (eventName, data, callback) {
56054 var socket = this.socket;
56055 var rootScope = this.$rootScope;
56056 this.socket.emit(eventName, data, function () {
56057 var args = arguments;
56058 rootScope.$apply(function () {
56060 callback.apply(socket, args);
56066 services.WebSocket = WebSocket;
56067 })(services = app.services || (app.services = {}));
56068 })(app || (app = {}));
56072 (function (services) {
56073 var Console = (function () {
56074 function Console(WebSocket, $rootScope) {
56075 this.WebSocket = WebSocket;
56076 this.$rootScope = $rootScope;
56077 this.WebSocket = WebSocket;
56078 this.$rootScope = $rootScope;
56079 this.directiveIDs = [];
56080 var directiveIDs = this.directiveIDs;
56081 this.WebSocket.on('console', function (d) {
56083 var message = d.message;
56084 if (directiveIDs.indexOf(id) > -1) {
56085 $rootScope.$emit(id, message);
56089 Console.prototype.addDirective = function (id) {
56090 if (!(this.directiveIDs.indexOf(id) > -1)) {
56091 this.directiveIDs.push(id);
56094 Console.prototype.removeDirective = function (id) {
56095 var i = this.directiveIDs.indexOf(id);
56097 this.directiveIDs.splice(i, 1);
56100 Console.prototype.showIDs = function () {
56101 console.log(this.directiveIDs);
56105 services.Console = Console;
56106 })(services = app.services || (app.services = {}));
56107 })(app || (app = {}));
56111 (function (directives) {
56112 var Command = (function () {
56113 function Command() {
56114 this.restrict = 'E';
56115 this.replace = true;
56117 this.controller = 'commandController';
56118 this.controllerAs = 'ctrl';
56119 this.bindToController = {
56125 this.templateUrl = 'templates/command.html';
56127 Command.Factory = function () {
56128 var directive = function () {
56129 return new Command();
56131 directive.$inject = [];
56136 directives.Command = Command;
56137 var CommandController = (function () {
56138 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
56139 this.APIEndPoint = APIEndPoint;
56140 this.$scope = $scope;
56141 this.MyModal = MyModal;
56142 this.WebSocket = WebSocket;
56143 this.$window = $window;
56144 this.$rootScope = $rootScope;
56145 this.Console = Console;
56146 var controller = this;
56148 .getOptionControlFile(this.name)
56150 .then(function (result) {
56151 controller.options = result.info;
56156 .then(function (result) {
56157 controller.dirs = result.info;
56159 this.heading = "[" + this.index + "]: dcdFilePrint";
56160 this.isOpen = true;
56161 this.$scope.$on('close', function () {
56162 controller.isOpen = false;
56166 return Math.floor((1 + Math.random()) * 0x10000)
56170 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
56171 s4() + '-' + s4() + s4() + s4();
56173 this.uuid = guid();
56174 this.Console.addDirective(this.uuid);
56175 this.Console.showIDs();
56177 CommandController.prototype.submit = function () {
56179 angular.forEach(this.options, function (option) {
56181 name: option.option,
56184 angular.forEach(option.arg, function (arg) {
56186 if (typeof arg.input === 'object') {
56187 obj.arguments.push(arg.input.name);
56190 obj.arguments.push(arg.input);
56194 if (obj.arguments.length > 0) {
56199 command: this.name,
56200 workspace: this.workspace.fileId,
56204 .execute(JSON.stringify(execObj))
56205 .then(function (result) {
56206 console.log(result);
56209 CommandController.prototype.removeMySelf = function (index) {
56210 this.$scope.$destroy();
56211 this.Console.removeDirective(this.uuid);
56212 this.remove()(index, this.list);
56213 this.Console.showIDs();
56215 CommandController.prototype.reloadFiles = function () {
56217 var fileId = this.workspace.fileId;
56221 .then(function (result) {
56222 var status = result.status;
56223 if (status === 'success') {
56224 _this.files = result.info;
56227 console.log(result.message);
56231 CommandController.prototype.debug = function () {
56232 var div = angular.element(this.$window.document).find("div");
56235 angular.forEach(div, function (v) {
56236 if (v.className === "panel-body console") {
56239 else if (v.className === "row parameters-console") {
56243 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
56244 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
56245 consoleTag.style.height = consoleHeight;
56246 consoleTag.style.width = consoleWidth;
56248 CommandController.prototype.help = function () {
56251 .then(function (result) {
56252 console.log(result);
56255 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
56256 return CommandController;
56258 directives.CommandController = CommandController;
56259 })(directives = app.directives || (app.directives = {}));
56260 })(app || (app = {}));
56264 (function (directives) {
56265 var HeaderMenu = (function () {
56266 function HeaderMenu() {
56267 this.restrict = 'E';
56268 this.replace = true;
56269 this.templateUrl = 'templates/header-menu.html';
56270 this.controller = 'HeaderMenuController';
56271 this.controllerAs = 'hmc';
56274 HeaderMenu.Factory = function () {
56275 var directive = function () {
56276 return new HeaderMenu();
56282 directives.HeaderMenu = HeaderMenu;
56283 var HeaderMenuController = (function () {
56284 function HeaderMenuController($state) {
56285 this.$state = $state;
56286 this.isExecution = this.$state.current.name === 'execution';
56287 this.isWorkspace = this.$state.current.name === 'workspace';
56288 this.isHistory = this.$state.current.name === 'history';
56290 HeaderMenuController.prototype.transit = function (state) {
56291 this.$state.go(state);
56293 HeaderMenuController.$inject = ['$state'];
56294 return HeaderMenuController;
56296 directives.HeaderMenuController = HeaderMenuController;
56297 })(directives = app.directives || (app.directives = {}));
56298 })(app || (app = {}));
56302 (function (directives) {
56303 var Option = (function () {
56304 function Option() {
56305 this.restrict = 'E';
56306 this.replace = true;
56307 this.controller = 'optionController';
56308 this.bindToController = {
56313 this.templateUrl = 'templates/option.html';
56314 this.controllerAs = 'ctrl';
56316 Option.Factory = function () {
56317 var directive = function () {
56318 return new Option();
56320 directive.$inject = [];
56325 directives.Option = Option;
56326 var OptionController = (function () {
56327 function OptionController() {
56328 var controller = this;
56329 angular.forEach(controller.info.arg, function (arg) {
56330 if (arg.initialValue) {
56331 if (arg.formType === 'number') {
56332 arg.input = parseInt(arg.initialValue);
56335 arg.input = arg.initialValue;
56340 OptionController.$inject = [];
56341 return OptionController;
56343 directives.OptionController = OptionController;
56344 })(directives = app.directives || (app.directives = {}));
56345 })(app || (app = {}));
56349 (function (directives) {
56350 var Directory = (function () {
56351 function Directory() {
56352 this.restrict = 'E';
56353 this.replace = true;
56354 this.controller = 'directoryController';
56355 this.controllerAs = 'ctrl';
56356 this.bindToController = {
56362 this.templateUrl = 'templates/directory.html';
56364 Directory.Factory = function () {
56365 var directive = function () {
56366 return new Directory();
56372 directives.Directory = Directory;
56373 var DirectoryController = (function () {
56374 function DirectoryController(APIEndPoint, $scope) {
56375 this.APIEndPoint = APIEndPoint;
56376 this.$scope = $scope;
56377 var controller = this;
56379 .getFiles(this.info.fileId)
56381 .then(function (result) {
56382 if (result.status === 'success') {
56383 controller.files = result.info;
56384 angular.forEach(result.info, function (file) {
56385 if (file.fileType === '0') {
56387 if (controller.info.path === '/') {
56388 o.path = '/' + file.name;
56391 o.path = controller.info.path + '/' + file.name;
56393 controller.add()(o, controller.list);
56400 DirectoryController.$inject = ['APIEndPoint', '$scope'];
56401 return DirectoryController;
56403 directives.DirectoryController = DirectoryController;
56404 })(directives = app.directives || (app.directives = {}));
56405 })(app || (app = {}));
56410 (function (directives) {
56411 var Upload = (function () {
56412 function Upload() {
56413 this.restrict = 'E';
56414 this.replace = true;
56416 this.controller = 'UploadController';
56417 this.controllerAs = 'ctrl';
56418 this.bindToController = {
56424 this.templateUrl = 'templates/upload.html';
56425 console.log("templates/upload.html-constructor");
56427 Upload.Factory = function () {
56428 var directive = function () {
56429 return new Upload();
56431 directive.$inject = [];
56436 directives.Upload = Upload;
56437 var UploadController = (function () {
56438 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
56439 this.APIEndPoint = APIEndPoint;
56440 this.$scope = $scope;
56441 this.MyModal = MyModal;
56442 this.WebSocket = WebSocket;
56443 this.$window = $window;
56444 this.$rootScope = $rootScope;
56445 this.Console = Console;
56446 var controller = this;
56447 console.log("directive.upload-constructor");
56449 UploadController.prototype.submit = function () {
56450 console.log("submit: function not supported¥n");
56452 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
56453 return UploadController;
56455 directives.UploadController = UploadController;
56456 })(directives = app.directives || (app.directives = {}));
56457 })(app || (app = {}));
56461 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56463 (function (controllers) {
56464 var Execution = (function () {
56465 function Execution(MyModal, $scope) {
56466 this.MyModal = MyModal;
56467 this.$scope = $scope;
56468 this.commandInfoList = [];
56471 Execution.prototype.add = function () {
56472 this.$scope.$broadcast('close');
56473 var commandInfoList = this.commandInfoList;
56474 var commandInstance = this.MyModal.selectCommand();
56477 .then(function (command) {
56478 commandInfoList.push(new app.declares.CommandInfo(command));
56481 Execution.prototype.open = function () {
56482 var result = this.MyModal.open('SelectCommand');
56483 console.log(result);
56485 Execution.prototype.remove = function (index, list) {
56486 list.splice(index, 1);
56488 Execution.prototype.close = function () {
56489 console.log("close");
56491 Execution.$inject = ['MyModal', '$scope'];
56494 controllers.Execution = Execution;
56495 })(controllers = app.controllers || (app.controllers = {}));
56496 })(app || (app = {}));
56500 (function (controllers) {
56501 var Workspace = (function () {
56502 function Workspace($scope, APIEndPoint, MyModal) {
56503 this.$scope = $scope;
56504 this.APIEndPoint = APIEndPoint;
56505 this.MyModal = MyModal;
56506 this.directoryList = [];
56507 var controller = this;
56508 var directoryList = this.directoryList;
56510 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
56518 directoryList.push(o);
56520 Workspace.prototype.addDirectory = function (info, directoryList) {
56521 directoryList.push(info);
56524 Workspace.prototype.upload = function () {
56525 this.MyModal.upload();
56528 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56529 Workspace.prototype.debug = function () {
56530 this.MyModal.preview();
56532 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
56535 controllers.Workspace = Workspace;
56536 })(controllers = app.controllers || (app.controllers = {}));
56537 })(app || (app = {}));
56541 (function (controllers) {
56542 var History = (function () {
56543 function History($scope) {
56544 this.page = "History";
56546 History.$inject = ['$scope'];
56549 controllers.History = History;
56550 })(controllers = app.controllers || (app.controllers = {}));
56551 })(app || (app = {}));
56555 (function (controllers) {
56556 var SelectCommand = (function () {
56557 function SelectCommand($scope, APIEndPoint, $modalInstance) {
56558 this.APIEndPoint = APIEndPoint;
56559 this.$modalInstance = $modalInstance;
56560 var controller = this;
56563 .$promise.then(function (result) {
56564 controller.tags = result.info;
56568 .$promise.then(function (result) {
56569 controller.commands = result.info;
56571 this.currentTag = 'all';
56573 SelectCommand.prototype.changeTag = function (tag) {
56574 this.currentTag = tag;
56576 SelectCommand.prototype.selectCommand = function (command) {
56577 this.$modalInstance.close(command);
56579 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
56580 return SelectCommand;
56582 controllers.SelectCommand = SelectCommand;
56583 })(controllers = app.controllers || (app.controllers = {}));
56584 })(app || (app = {}));
56588 (function (controllers) {
56590 var Upload = (function () {
56591 function Upload($scope, APIEndPoint, $modalInstance) {
56592 this.APIEndPoint = APIEndPoint;
56593 this.$modalInstance = $modalInstance;
56594 var controller = this;
56595 console.log('controller.upload-controllers');
56597 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
56600 controllers.Upload = Upload;
56601 })(controllers = app.controllers || (app.controllers = {}));
56602 })(app || (app = {}));
56606 (function (controllers) {
56608 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56609 var Preview = (function () {
56610 function Preview($scope, APIEndPoint, $modalInstance) {
56611 this.APIEndPoint = APIEndPoint;
56612 this.$modalInstance = $modalInstance;
56613 var controller = this;
56614 console.log('preview');
56616 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
56619 controllers.Preview = Preview;
56620 })(controllers = app.controllers || (app.controllers = {}));
56621 })(app || (app = {}));
56623 (function (filters) {
56625 return function (commands, tag) {
56627 angular.forEach(commands, function (command) {
56629 angular.forEach(command.tags, function (value) {
56634 result.push(command);
56640 })(filters || (filters = {}));
56644 var appName = 'zephyr';
56645 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
56646 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
56647 $urlRouterProvider.otherwise('/execution');
56648 $locationProvider.html5Mode({
56653 .state('execution', {
56655 templateUrl: 'templates/execution.html',
56656 controller: 'executionController',
56659 .state('workspace', {
56661 templateUrl: 'templates/workspace.html',
56662 controller: 'workspaceController',
56665 .state('history', {
56667 templateUrl: 'templates/history.html',
56668 controller: 'historyController',
56672 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
56673 app.zephyr.service('MyModal', app.services.MyModal);
56674 app.zephyr.service('WebSocket', app.services.WebSocket);
56675 app.zephyr.service('Console', app.services.Console);
56676 app.zephyr.filter('Tag', filters.Tag);
56677 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
56678 app.zephyr.controller('previewController', app.controllers.Preview);
56680 app.zephyr.controller('uploadController', app.controllers.Upload);
56682 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56683 app.zephyr.controller('executionController', app.controllers.Execution);
56684 app.zephyr.controller('workspaceController', app.controllers.Workspace);
56685 app.zephyr.controller('historyController', app.controllers.History);
56686 app.zephyr.controller('commandController', app.directives.CommandController);
56687 app.zephyr.controller('optionController', app.directives.OptionController);
56688 app.zephyr.controller('directoryController', app.directives.DirectoryController);
56689 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
56691 app.zephyr.controller('uploadController', app.directives.UploadController);
56693 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56694 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
56695 app.zephyr.directive('command', app.directives.Command.Factory());
56696 app.zephyr.directive('option', app.directives.Option.Factory());
56697 app.zephyr.directive('directory', app.directives.Directory.Factory());
56698 })(app || (app = {}));
56706 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56707 /***/ function(module, exports) {
56712 (function (declares) {
56713 var CommandInfo = (function () {
56714 function CommandInfo(name) {
56717 return CommandInfo;
56719 declares.CommandInfo = CommandInfo;
56720 })(declares = app.declares || (app.declares = {}));
56721 })(app || (app = {}));
56725 (function (services) {
56726 var APIEndPoint = (function () {
56727 function APIEndPoint($resource, $http) {
56728 this.$resource = $resource;
56729 this.$http = $http;
56731 APIEndPoint.prototype.resource = function (endPoint, data) {
56732 var customAction = {
56738 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
56740 return this.$resource(endPoint, {}, { execute: execute });
56742 APIEndPoint.prototype.getOptionControlFile = function (command) {
56743 var endPoint = '/api/v1/optionControlFile/' + command;
56744 return this.resource(endPoint, {}).get();
56746 APIEndPoint.prototype.getFiles = function (fileId) {
56747 var endPoint = '/api/v1/workspace';
56749 endPoint += '/' + fileId;
56751 return this.resource(endPoint, {}).get();
56753 APIEndPoint.prototype.getDirectories = function () {
56754 var endPoint = '/api/v1/all/workspace/directory';
56755 return this.resource(endPoint, {}).get();
56757 APIEndPoint.prototype.getTags = function () {
56758 var endPoint = '/api/v1/tagList';
56759 return this.resource(endPoint, {}).get();
56761 APIEndPoint.prototype.getCommands = function () {
56762 var endPoint = '/api/v1/commandList';
56763 return this.resource(endPoint, {}).get();
56765 APIEndPoint.prototype.execute = function (data) {
56766 var endPoint = '/api/v1/execution';
56767 var fd = new FormData();
56768 fd.append('data', data);
56769 return this.$http.post(endPoint, fd, {
56770 headers: { 'Content-Type': undefined },
56771 transformRequest: angular.identity
56774 APIEndPoint.prototype.debug = function () {
56775 var endPoint = '/api/v1/debug';
56776 return this.$http.get(endPoint);
56779 APIEndPoint.prototype.upload = function () {
56780 var endPoint = '/api/v1/upload';
56781 return this.$http.get(endPoint);
56784 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56785 APIEndPoint.prototype.help = function (command) {
56786 var endPoint = '/api/v1/help/' + command;
56787 return this.$http.get(endPoint);
56789 return APIEndPoint;
56791 services.APIEndPoint = APIEndPoint;
56792 })(services = app.services || (app.services = {}));
56793 })(app || (app = {}));
56797 (function (services) {
56798 var MyModal = (function () {
56799 function MyModal($uibModal) {
56800 this.$uibModal = $uibModal;
56801 this.modalOption = {
56808 MyModal.prototype.open = function (modalName) {
56809 if (modalName === 'SelectCommand') {
56810 this.modalOption.templateUrl = 'templates/select-command.html';
56811 this.modalOption.size = 'lg';
56813 return this.$uibModal.open(this.modalOption);
56815 MyModal.prototype.selectCommand = function () {
56816 this.modalOption.templateUrl = 'templates/select-command.html';
56817 this.modalOption.controller = 'selectCommandController';
56818 this.modalOption.controllerAs = 'c';
56819 this.modalOption.size = 'lg';
56820 return this.$uibModal.open(this.modalOption);
56822 MyModal.prototype.preview = function () {
56823 this.modalOption.templateUrl = 'templates/preview.html';
56824 this.modalOption.controller = 'previewController';
56825 this.modalOption.controllerAs = 'c';
56826 this.modalOption.size = 'lg';
56827 return this.$uibModal.open(this.modalOption);
56830 MyModal.prototype.upload = function () {
56831 this.modalOption.templateUrl = 'templates/upload.html';
56832 this.modalOption.controller = 'uploadController';
56833 this.modalOption.controllerAs = 'c';
56834 this.modalOption.size = 'lg';
56835 return this.$uibModal.open(this.modalOption);
56838 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
56839 MyModal.$inject = ['$uibModal'];
56842 services.MyModal = MyModal;
56843 })(services = app.services || (app.services = {}));
56844 })(app || (app = {}));
56848 (function (services) {
56849 var WebSocket = (function () {
56850 function WebSocket($rootScope) {
56851 this.$rootScope = $rootScope;
56852 this.socket = io.connect();
56854 WebSocket.prototype.on = function (eventName, callback) {
56855 var socket = this.socket;
56856 var rootScope = this.$rootScope;
56857 socket.on(eventName, function () {
56858 var args = arguments;
56859 rootScope.$apply(function () {
56860 callback.apply(socket, args);
56864 WebSocket.prototype.emit = function (eventName, data, callback) {
56865 var socket = this.socket;
56866 var rootScope = this.$rootScope;
56867 this.socket.emit(eventName, data, function () {
56868 var args = arguments;
56869 rootScope.$apply(function () {
56871 callback.apply(socket, args);
56877 services.WebSocket = WebSocket;
56878 })(services = app.services || (app.services = {}));
56879 })(app || (app = {}));
56883 (function (services) {
56884 var Console = (function () {
56885 function Console(WebSocket, $rootScope) {
56886 this.WebSocket = WebSocket;
56887 this.$rootScope = $rootScope;
56888 this.WebSocket = WebSocket;
56889 this.$rootScope = $rootScope;
56890 this.directiveIDs = [];
56891 var directiveIDs = this.directiveIDs;
56892 this.WebSocket.on('console', function (d) {
56894 var message = d.message;
56895 if (directiveIDs.indexOf(id) > -1) {
56896 $rootScope.$emit(id, message);
56900 Console.prototype.addDirective = function (id) {
56901 if (!(this.directiveIDs.indexOf(id) > -1)) {
56902 this.directiveIDs.push(id);
56905 Console.prototype.removeDirective = function (id) {
56906 var i = this.directiveIDs.indexOf(id);
56908 this.directiveIDs.splice(i, 1);
56911 Console.prototype.showIDs = function () {
56912 console.log(this.directiveIDs);
56916 services.Console = Console;
56917 })(services = app.services || (app.services = {}));
56918 })(app || (app = {}));
56922 (function (directives) {
56923 var Command = (function () {
56924 function Command() {
56925 this.restrict = 'E';
56926 this.replace = true;
56928 this.controller = 'commandController';
56929 this.controllerAs = 'ctrl';
56930 this.bindToController = {
56936 this.templateUrl = 'templates/command.html';
56938 Command.Factory = function () {
56939 var directive = function () {
56940 return new Command();
56942 directive.$inject = [];
56947 directives.Command = Command;
56948 var CommandController = (function () {
56949 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
56950 this.APIEndPoint = APIEndPoint;
56951 this.$scope = $scope;
56952 this.MyModal = MyModal;
56953 this.WebSocket = WebSocket;
56954 this.$window = $window;
56955 this.$rootScope = $rootScope;
56956 this.Console = Console;
56957 var controller = this;
56959 .getOptionControlFile(this.name)
56961 .then(function (result) {
56962 controller.options = result.info;
56967 .then(function (result) {
56968 controller.dirs = result.info;
56970 this.heading = "[" + this.index + "]: dcdFilePrint";
56971 this.isOpen = true;
56972 this.$scope.$on('close', function () {
56973 controller.isOpen = false;
56977 return Math.floor((1 + Math.random()) * 0x10000)
56981 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
56982 s4() + '-' + s4() + s4() + s4();
56984 this.uuid = guid();
56985 this.Console.addDirective(this.uuid);
56986 this.Console.showIDs();
56988 CommandController.prototype.submit = function () {
56990 angular.forEach(this.options, function (option) {
56992 name: option.option,
56995 angular.forEach(option.arg, function (arg) {
56997 if (typeof arg.input === 'object') {
56998 obj.arguments.push(arg.input.name);
57001 obj.arguments.push(arg.input);
57005 if (obj.arguments.length > 0) {
57010 command: this.name,
57011 workspace: this.workspace.fileId,
57015 .execute(JSON.stringify(execObj))
57016 .then(function (result) {
57017 console.log(result);
57020 CommandController.prototype.removeMySelf = function (index) {
57021 this.$scope.$destroy();
57022 this.Console.removeDirective(this.uuid);
57023 this.remove()(index, this.list);
57024 this.Console.showIDs();
57026 CommandController.prototype.reloadFiles = function () {
57028 var fileId = this.workspace.fileId;
57032 .then(function (result) {
57033 var status = result.status;
57034 if (status === 'success') {
57035 _this.files = result.info;
57038 console.log(result.message);
57042 CommandController.prototype.debug = function () {
57043 var div = angular.element(this.$window.document).find("div");
57046 angular.forEach(div, function (v) {
57047 if (v.className === "panel-body console") {
57050 else if (v.className === "row parameters-console") {
57054 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
57055 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
57056 consoleTag.style.height = consoleHeight;
57057 consoleTag.style.width = consoleWidth;
57059 CommandController.prototype.help = function () {
57062 .then(function (result) {
57063 console.log(result);
57066 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
57067 return CommandController;
57069 directives.CommandController = CommandController;
57070 })(directives = app.directives || (app.directives = {}));
57071 })(app || (app = {}));
57075 (function (directives) {
57076 var HeaderMenu = (function () {
57077 function HeaderMenu() {
57078 this.restrict = 'E';
57079 this.replace = true;
57080 this.templateUrl = 'templates/header-menu.html';
57081 this.controller = 'HeaderMenuController';
57082 this.controllerAs = 'hmc';
57085 HeaderMenu.Factory = function () {
57086 var directive = function () {
57087 return new HeaderMenu();
57093 directives.HeaderMenu = HeaderMenu;
57094 var HeaderMenuController = (function () {
57095 function HeaderMenuController($state) {
57096 this.$state = $state;
57097 this.isExecution = this.$state.current.name === 'execution';
57098 this.isWorkspace = this.$state.current.name === 'workspace';
57099 this.isHistory = this.$state.current.name === 'history';
57101 HeaderMenuController.prototype.transit = function (state) {
57102 this.$state.go(state);
57104 HeaderMenuController.$inject = ['$state'];
57105 return HeaderMenuController;
57107 directives.HeaderMenuController = HeaderMenuController;
57108 })(directives = app.directives || (app.directives = {}));
57109 })(app || (app = {}));
57113 (function (directives) {
57114 var Option = (function () {
57115 function Option() {
57116 this.restrict = 'E';
57117 this.replace = true;
57118 this.controller = 'optionController';
57119 this.bindToController = {
57124 this.templateUrl = 'templates/option.html';
57125 this.controllerAs = 'ctrl';
57127 Option.Factory = function () {
57128 var directive = function () {
57129 return new Option();
57131 directive.$inject = [];
57136 directives.Option = Option;
57137 var OptionController = (function () {
57138 function OptionController() {
57139 var controller = this;
57140 angular.forEach(controller.info.arg, function (arg) {
57141 if (arg.initialValue) {
57142 if (arg.formType === 'number') {
57143 arg.input = parseInt(arg.initialValue);
57146 arg.input = arg.initialValue;
57151 OptionController.$inject = [];
57152 return OptionController;
57154 directives.OptionController = OptionController;
57155 })(directives = app.directives || (app.directives = {}));
57156 })(app || (app = {}));
57160 (function (directives) {
57161 var Directory = (function () {
57162 function Directory() {
57163 this.restrict = 'E';
57164 this.replace = true;
57165 this.controller = 'directoryController';
57166 this.controllerAs = 'ctrl';
57167 this.bindToController = {
57173 this.templateUrl = 'templates/directory.html';
57175 Directory.Factory = function () {
57176 var directive = function () {
57177 return new Directory();
57183 directives.Directory = Directory;
57184 var DirectoryController = (function () {
57185 function DirectoryController(APIEndPoint, $scope) {
57186 this.APIEndPoint = APIEndPoint;
57187 this.$scope = $scope;
57188 var controller = this;
57190 .getFiles(this.info.fileId)
57192 .then(function (result) {
57193 if (result.status === 'success') {
57194 controller.files = result.info;
57195 angular.forEach(result.info, function (file) {
57196 if (file.fileType === '0') {
57198 if (controller.info.path === '/') {
57199 o.path = '/' + file.name;
57202 o.path = controller.info.path + '/' + file.name;
57204 controller.add()(o, controller.list);
57211 DirectoryController.$inject = ['APIEndPoint', '$scope'];
57212 return DirectoryController;
57214 directives.DirectoryController = DirectoryController;
57215 })(directives = app.directives || (app.directives = {}));
57216 })(app || (app = {}));
57221 (function (directives) {
57222 var Upload = (function () {
57223 function Upload() {
57224 this.restrict = 'E';
57225 this.replace = true;
57227 this.controller = 'UploadController';
57228 this.controllerAs = 'ctrl';
57229 this.bindToController = {
57235 this.templateUrl = 'templates/upload.html';
57236 console.log("templates/upload.html-constructor");
57238 Upload.Factory = function () {
57239 var directive = function () {
57240 return new Upload();
57242 directive.$inject = [];
57247 directives.Upload = Upload;
57248 var UploadController = (function () {
57249 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
57250 this.APIEndPoint = APIEndPoint;
57251 this.$scope = $scope;
57252 this.MyModal = MyModal;
57253 this.WebSocket = WebSocket;
57254 this.$window = $window;
57255 this.$rootScope = $rootScope;
57256 this.Console = Console;
57257 var controller = this;
57258 console.log("directive.upload-constructor");
57260 UploadController.prototype.submit = function () {
57261 console.log("submit: function not supported¥n");
57263 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
57264 return UploadController;
57266 directives.UploadController = UploadController;
57267 })(directives = app.directives || (app.directives = {}));
57268 })(app || (app = {}));
57272 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57274 (function (controllers) {
57275 var Execution = (function () {
57276 function Execution(MyModal, $scope) {
57277 this.MyModal = MyModal;
57278 this.$scope = $scope;
57279 this.commandInfoList = [];
57282 Execution.prototype.add = function () {
57283 this.$scope.$broadcast('close');
57284 var commandInfoList = this.commandInfoList;
57285 var commandInstance = this.MyModal.selectCommand();
57288 .then(function (command) {
57289 commandInfoList.push(new app.declares.CommandInfo(command));
57292 Execution.prototype.open = function () {
57293 var result = this.MyModal.open('SelectCommand');
57294 console.log(result);
57296 Execution.prototype.remove = function (index, list) {
57297 list.splice(index, 1);
57299 Execution.prototype.close = function () {
57300 console.log("close");
57302 Execution.$inject = ['MyModal', '$scope'];
57305 controllers.Execution = Execution;
57306 })(controllers = app.controllers || (app.controllers = {}));
57307 })(app || (app = {}));
57311 (function (controllers) {
57312 var Workspace = (function () {
57313 function Workspace($scope, APIEndPoint, MyModal) {
57314 this.$scope = $scope;
57315 this.APIEndPoint = APIEndPoint;
57316 this.MyModal = MyModal;
57317 this.directoryList = [];
57318 var controller = this;
57319 var directoryList = this.directoryList;
57321 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
57329 directoryList.push(o);
57331 Workspace.prototype.addDirectory = function (info, directoryList) {
57332 directoryList.push(info);
57335 Workspace.prototype.upload = function () {
57336 this.MyModal.upload();
57339 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57340 Workspace.prototype.debug = function () {
57341 this.MyModal.preview();
57343 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
57346 controllers.Workspace = Workspace;
57347 })(controllers = app.controllers || (app.controllers = {}));
57348 })(app || (app = {}));
57352 (function (controllers) {
57353 var History = (function () {
57354 function History($scope) {
57355 this.page = "History";
57357 History.$inject = ['$scope'];
57360 controllers.History = History;
57361 })(controllers = app.controllers || (app.controllers = {}));
57362 })(app || (app = {}));
57366 (function (controllers) {
57367 var SelectCommand = (function () {
57368 function SelectCommand($scope, APIEndPoint, $modalInstance) {
57369 this.APIEndPoint = APIEndPoint;
57370 this.$modalInstance = $modalInstance;
57371 var controller = this;
57374 .$promise.then(function (result) {
57375 controller.tags = result.info;
57379 .$promise.then(function (result) {
57380 controller.commands = result.info;
57382 this.currentTag = 'all';
57384 SelectCommand.prototype.changeTag = function (tag) {
57385 this.currentTag = tag;
57387 SelectCommand.prototype.selectCommand = function (command) {
57388 this.$modalInstance.close(command);
57390 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
57391 return SelectCommand;
57393 controllers.SelectCommand = SelectCommand;
57394 })(controllers = app.controllers || (app.controllers = {}));
57395 })(app || (app = {}));
57399 (function (controllers) {
57401 var Upload = (function () {
57402 function Upload($scope, APIEndPoint, $modalInstance) {
57403 this.APIEndPoint = APIEndPoint;
57404 this.$modalInstance = $modalInstance;
57405 var controller = this;
57406 console.log('controller.upload-controllers');
57408 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
57411 controllers.Upload = Upload;
57412 })(controllers = app.controllers || (app.controllers = {}));
57413 })(app || (app = {}));
57417 (function (controllers) {
57419 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57420 var Preview = (function () {
57421 function Preview($scope, APIEndPoint, $modalInstance) {
57422 this.APIEndPoint = APIEndPoint;
57423 this.$modalInstance = $modalInstance;
57424 var controller = this;
57425 console.log('preview');
57427 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
57430 controllers.Preview = Preview;
57431 })(controllers = app.controllers || (app.controllers = {}));
57432 })(app || (app = {}));
57434 (function (filters) {
57436 return function (commands, tag) {
57438 angular.forEach(commands, function (command) {
57440 angular.forEach(command.tags, function (value) {
57445 result.push(command);
57451 })(filters || (filters = {}));
57455 var appName = 'zephyr';
57456 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
57457 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
57458 $urlRouterProvider.otherwise('/execution');
57459 $locationProvider.html5Mode({
57464 .state('execution', {
57466 templateUrl: 'templates/execution.html',
57467 controller: 'executionController',
57470 .state('workspace', {
57472 templateUrl: 'templates/workspace.html',
57473 controller: 'workspaceController',
57476 .state('history', {
57478 templateUrl: 'templates/history.html',
57479 controller: 'historyController',
57483 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
57484 app.zephyr.service('MyModal', app.services.MyModal);
57485 app.zephyr.service('WebSocket', app.services.WebSocket);
57486 app.zephyr.service('Console', app.services.Console);
57487 app.zephyr.filter('Tag', filters.Tag);
57488 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
57489 app.zephyr.controller('previewController', app.controllers.Preview);
57491 app.zephyr.controller('uploadController', app.controllers.Upload);
57493 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57494 app.zephyr.controller('executionController', app.controllers.Execution);
57495 app.zephyr.controller('workspaceController', app.controllers.Workspace);
57496 app.zephyr.controller('historyController', app.controllers.History);
57497 app.zephyr.controller('commandController', app.directives.CommandController);
57498 app.zephyr.controller('optionController', app.directives.OptionController);
57499 app.zephyr.controller('directoryController', app.directives.DirectoryController);
57500 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
57502 app.zephyr.controller('uploadController', app.directives.UploadController);
57504 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57505 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
57506 app.zephyr.directive('command', app.directives.Command.Factory());
57507 app.zephyr.directive('option', app.directives.Option.Factory());
57508 app.zephyr.directive('directory', app.directives.Directory.Factory());
57509 })(app || (app = {}));
57517 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57518 /***/ function(module, exports) {
57523 (function (declares) {
57524 var CommandInfo = (function () {
57525 function CommandInfo(name) {
57528 return CommandInfo;
57530 declares.CommandInfo = CommandInfo;
57531 })(declares = app.declares || (app.declares = {}));
57532 })(app || (app = {}));
57536 (function (services) {
57537 var APIEndPoint = (function () {
57538 function APIEndPoint($resource, $http) {
57539 this.$resource = $resource;
57540 this.$http = $http;
57542 APIEndPoint.prototype.resource = function (endPoint, data) {
57543 var customAction = {
57549 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
57551 return this.$resource(endPoint, {}, { execute: execute });
57553 APIEndPoint.prototype.getOptionControlFile = function (command) {
57554 var endPoint = '/api/v1/optionControlFile/' + command;
57555 return this.resource(endPoint, {}).get();
57557 APIEndPoint.prototype.getFiles = function (fileId) {
57558 var endPoint = '/api/v1/workspace';
57560 endPoint += '/' + fileId;
57562 return this.resource(endPoint, {}).get();
57564 APIEndPoint.prototype.getDirectories = function () {
57565 var endPoint = '/api/v1/all/workspace/directory';
57566 return this.resource(endPoint, {}).get();
57568 APIEndPoint.prototype.getTags = function () {
57569 var endPoint = '/api/v1/tagList';
57570 return this.resource(endPoint, {}).get();
57572 APIEndPoint.prototype.getCommands = function () {
57573 var endPoint = '/api/v1/commandList';
57574 return this.resource(endPoint, {}).get();
57576 APIEndPoint.prototype.execute = function (data) {
57577 var endPoint = '/api/v1/execution';
57578 var fd = new FormData();
57579 fd.append('data', data);
57580 return this.$http.post(endPoint, fd, {
57581 headers: { 'Content-Type': undefined },
57582 transformRequest: angular.identity
57585 APIEndPoint.prototype.debug = function () {
57586 var endPoint = '/api/v1/debug';
57587 return this.$http.get(endPoint);
57590 APIEndPoint.prototype.upload = function () {
57591 var endPoint = '/api/v1/upload';
57592 return this.$http.get(endPoint);
57595 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57596 APIEndPoint.prototype.help = function (command) {
57597 var endPoint = '/api/v1/help/' + command;
57598 return this.$http.get(endPoint);
57600 return APIEndPoint;
57602 services.APIEndPoint = APIEndPoint;
57603 })(services = app.services || (app.services = {}));
57604 })(app || (app = {}));
57608 (function (services) {
57609 var MyModal = (function () {
57610 function MyModal($uibModal) {
57611 this.$uibModal = $uibModal;
57612 this.modalOption = {
57619 MyModal.prototype.open = function (modalName) {
57620 if (modalName === 'SelectCommand') {
57621 this.modalOption.templateUrl = 'templates/select-command.html';
57622 this.modalOption.size = 'lg';
57624 return this.$uibModal.open(this.modalOption);
57626 MyModal.prototype.selectCommand = function () {
57627 this.modalOption.templateUrl = 'templates/select-command.html';
57628 this.modalOption.controller = 'selectCommandController';
57629 this.modalOption.controllerAs = 'c';
57630 this.modalOption.size = 'lg';
57631 return this.$uibModal.open(this.modalOption);
57633 MyModal.prototype.preview = function () {
57634 this.modalOption.templateUrl = 'templates/preview.html';
57635 this.modalOption.controller = 'previewController';
57636 this.modalOption.controllerAs = 'c';
57637 this.modalOption.size = 'lg';
57638 return this.$uibModal.open(this.modalOption);
57641 MyModal.prototype.upload = function () {
57642 this.modalOption.templateUrl = 'templates/upload.html';
57643 this.modalOption.controller = 'uploadController';
57644 this.modalOption.controllerAs = 'c';
57645 this.modalOption.size = 'lg';
57646 return this.$uibModal.open(this.modalOption);
57649 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
57650 MyModal.$inject = ['$uibModal'];
57653 services.MyModal = MyModal;
57654 })(services = app.services || (app.services = {}));
57655 })(app || (app = {}));
57659 (function (services) {
57660 var WebSocket = (function () {
57661 function WebSocket($rootScope) {
57662 this.$rootScope = $rootScope;
57663 this.socket = io.connect();
57665 WebSocket.prototype.on = function (eventName, callback) {
57666 var socket = this.socket;
57667 var rootScope = this.$rootScope;
57668 socket.on(eventName, function () {
57669 var args = arguments;
57670 rootScope.$apply(function () {
57671 callback.apply(socket, args);
57675 WebSocket.prototype.emit = function (eventName, data, callback) {
57676 var socket = this.socket;
57677 var rootScope = this.$rootScope;
57678 this.socket.emit(eventName, data, function () {
57679 var args = arguments;
57680 rootScope.$apply(function () {
57682 callback.apply(socket, args);
57688 services.WebSocket = WebSocket;
57689 })(services = app.services || (app.services = {}));
57690 })(app || (app = {}));
57694 (function (services) {
57695 var Console = (function () {
57696 function Console(WebSocket, $rootScope) {
57697 this.WebSocket = WebSocket;
57698 this.$rootScope = $rootScope;
57699 this.WebSocket = WebSocket;
57700 this.$rootScope = $rootScope;
57701 this.directiveIDs = [];
57702 var directiveIDs = this.directiveIDs;
57703 this.WebSocket.on('console', function (d) {
57705 var message = d.message;
57706 if (directiveIDs.indexOf(id) > -1) {
57707 $rootScope.$emit(id, message);
57711 Console.prototype.addDirective = function (id) {
57712 if (!(this.directiveIDs.indexOf(id) > -1)) {
57713 this.directiveIDs.push(id);
57716 Console.prototype.removeDirective = function (id) {
57717 var i = this.directiveIDs.indexOf(id);
57719 this.directiveIDs.splice(i, 1);
57722 Console.prototype.showIDs = function () {
57723 console.log(this.directiveIDs);
57727 services.Console = Console;
57728 })(services = app.services || (app.services = {}));
57729 })(app || (app = {}));
57733 (function (directives) {
57734 var Command = (function () {
57735 function Command() {
57736 this.restrict = 'E';
57737 this.replace = true;
57739 this.controller = 'commandController';
57740 this.controllerAs = 'ctrl';
57741 this.bindToController = {
57747 this.templateUrl = 'templates/command.html';
57749 Command.Factory = function () {
57750 var directive = function () {
57751 return new Command();
57753 directive.$inject = [];
57758 directives.Command = Command;
57759 var CommandController = (function () {
57760 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
57761 this.APIEndPoint = APIEndPoint;
57762 this.$scope = $scope;
57763 this.MyModal = MyModal;
57764 this.WebSocket = WebSocket;
57765 this.$window = $window;
57766 this.$rootScope = $rootScope;
57767 this.Console = Console;
57768 var controller = this;
57770 .getOptionControlFile(this.name)
57772 .then(function (result) {
57773 controller.options = result.info;
57778 .then(function (result) {
57779 controller.dirs = result.info;
57781 this.heading = "[" + this.index + "]: dcdFilePrint";
57782 this.isOpen = true;
57783 this.$scope.$on('close', function () {
57784 controller.isOpen = false;
57788 return Math.floor((1 + Math.random()) * 0x10000)
57792 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
57793 s4() + '-' + s4() + s4() + s4();
57795 this.uuid = guid();
57796 this.Console.addDirective(this.uuid);
57797 this.Console.showIDs();
57799 CommandController.prototype.submit = function () {
57801 angular.forEach(this.options, function (option) {
57803 name: option.option,
57806 angular.forEach(option.arg, function (arg) {
57808 if (typeof arg.input === 'object') {
57809 obj.arguments.push(arg.input.name);
57812 obj.arguments.push(arg.input);
57816 if (obj.arguments.length > 0) {
57821 command: this.name,
57822 workspace: this.workspace.fileId,
57826 .execute(JSON.stringify(execObj))
57827 .then(function (result) {
57828 console.log(result);
57831 CommandController.prototype.removeMySelf = function (index) {
57832 this.$scope.$destroy();
57833 this.Console.removeDirective(this.uuid);
57834 this.remove()(index, this.list);
57835 this.Console.showIDs();
57837 CommandController.prototype.reloadFiles = function () {
57839 var fileId = this.workspace.fileId;
57843 .then(function (result) {
57844 var status = result.status;
57845 if (status === 'success') {
57846 _this.files = result.info;
57849 console.log(result.message);
57853 CommandController.prototype.debug = function () {
57854 var div = angular.element(this.$window.document).find("div");
57857 angular.forEach(div, function (v) {
57858 if (v.className === "panel-body console") {
57861 else if (v.className === "row parameters-console") {
57865 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
57866 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
57867 consoleTag.style.height = consoleHeight;
57868 consoleTag.style.width = consoleWidth;
57870 CommandController.prototype.help = function () {
57873 .then(function (result) {
57874 console.log(result);
57877 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
57878 return CommandController;
57880 directives.CommandController = CommandController;
57881 })(directives = app.directives || (app.directives = {}));
57882 })(app || (app = {}));
57886 (function (directives) {
57887 var HeaderMenu = (function () {
57888 function HeaderMenu() {
57889 this.restrict = 'E';
57890 this.replace = true;
57891 this.templateUrl = 'templates/header-menu.html';
57892 this.controller = 'HeaderMenuController';
57893 this.controllerAs = 'hmc';
57896 HeaderMenu.Factory = function () {
57897 var directive = function () {
57898 return new HeaderMenu();
57904 directives.HeaderMenu = HeaderMenu;
57905 var HeaderMenuController = (function () {
57906 function HeaderMenuController($state) {
57907 this.$state = $state;
57908 this.isExecution = this.$state.current.name === 'execution';
57909 this.isWorkspace = this.$state.current.name === 'workspace';
57910 this.isHistory = this.$state.current.name === 'history';
57912 HeaderMenuController.prototype.transit = function (state) {
57913 this.$state.go(state);
57915 HeaderMenuController.$inject = ['$state'];
57916 return HeaderMenuController;
57918 directives.HeaderMenuController = HeaderMenuController;
57919 })(directives = app.directives || (app.directives = {}));
57920 })(app || (app = {}));
57924 (function (directives) {
57925 var Option = (function () {
57926 function Option() {
57927 this.restrict = 'E';
57928 this.replace = true;
57929 this.controller = 'optionController';
57930 this.bindToController = {
57935 this.templateUrl = 'templates/option.html';
57936 this.controllerAs = 'ctrl';
57938 Option.Factory = function () {
57939 var directive = function () {
57940 return new Option();
57942 directive.$inject = [];
57947 directives.Option = Option;
57948 var OptionController = (function () {
57949 function OptionController() {
57950 var controller = this;
57951 angular.forEach(controller.info.arg, function (arg) {
57952 if (arg.initialValue) {
57953 if (arg.formType === 'number') {
57954 arg.input = parseInt(arg.initialValue);
57957 arg.input = arg.initialValue;
57962 OptionController.$inject = [];
57963 return OptionController;
57965 directives.OptionController = OptionController;
57966 })(directives = app.directives || (app.directives = {}));
57967 })(app || (app = {}));
57971 (function (directives) {
57972 var Directory = (function () {
57973 function Directory() {
57974 this.restrict = 'E';
57975 this.replace = true;
57976 this.controller = 'directoryController';
57977 this.controllerAs = 'ctrl';
57978 this.bindToController = {
57984 this.templateUrl = 'templates/directory.html';
57986 Directory.Factory = function () {
57987 var directive = function () {
57988 return new Directory();
57994 directives.Directory = Directory;
57995 var DirectoryController = (function () {
57996 function DirectoryController(APIEndPoint, $scope) {
57997 this.APIEndPoint = APIEndPoint;
57998 this.$scope = $scope;
57999 var controller = this;
58001 .getFiles(this.info.fileId)
58003 .then(function (result) {
58004 if (result.status === 'success') {
58005 controller.files = result.info;
58006 angular.forEach(result.info, function (file) {
58007 if (file.fileType === '0') {
58009 if (controller.info.path === '/') {
58010 o.path = '/' + file.name;
58013 o.path = controller.info.path + '/' + file.name;
58015 controller.add()(o, controller.list);
58022 DirectoryController.$inject = ['APIEndPoint', '$scope'];
58023 return DirectoryController;
58025 directives.DirectoryController = DirectoryController;
58026 })(directives = app.directives || (app.directives = {}));
58027 })(app || (app = {}));
58032 (function (directives) {
58033 var Upload = (function () {
58034 function Upload() {
58035 this.restrict = 'E';
58036 this.replace = true;
58038 this.controller = 'UploadController';
58039 this.controllerAs = 'ctrl';
58040 this.bindToController = {
58046 this.templateUrl = 'templates/upload.html';
58047 console.log("templates/upload.html-constructor");
58049 Upload.Factory = function () {
58050 var directive = function () {
58051 return new Upload();
58053 directive.$inject = [];
58058 directives.Upload = Upload;
58059 var UploadController = (function () {
58060 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
58061 this.APIEndPoint = APIEndPoint;
58062 this.$scope = $scope;
58063 this.MyModal = MyModal;
58064 this.WebSocket = WebSocket;
58065 this.$window = $window;
58066 this.$rootScope = $rootScope;
58067 this.Console = Console;
58068 var controller = this;
58069 console.log("directive.upload-constructor");
58071 UploadController.prototype.submit = function () {
58072 console.log("submit: function not supported¥n");
58074 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
58075 return UploadController;
58077 directives.UploadController = UploadController;
58078 })(directives = app.directives || (app.directives = {}));
58079 })(app || (app = {}));
58083 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58085 (function (controllers) {
58086 var Execution = (function () {
58087 function Execution(MyModal, $scope) {
58088 this.MyModal = MyModal;
58089 this.$scope = $scope;
58090 this.commandInfoList = [];
58093 Execution.prototype.add = function () {
58094 this.$scope.$broadcast('close');
58095 var commandInfoList = this.commandInfoList;
58096 var commandInstance = this.MyModal.selectCommand();
58099 .then(function (command) {
58100 commandInfoList.push(new app.declares.CommandInfo(command));
58103 Execution.prototype.open = function () {
58104 var result = this.MyModal.open('SelectCommand');
58105 console.log(result);
58107 Execution.prototype.remove = function (index, list) {
58108 list.splice(index, 1);
58110 Execution.prototype.close = function () {
58111 console.log("close");
58113 Execution.$inject = ['MyModal', '$scope'];
58116 controllers.Execution = Execution;
58117 })(controllers = app.controllers || (app.controllers = {}));
58118 })(app || (app = {}));
58122 (function (controllers) {
58123 var Workspace = (function () {
58124 function Workspace($scope, APIEndPoint, MyModal) {
58125 this.$scope = $scope;
58126 this.APIEndPoint = APIEndPoint;
58127 this.MyModal = MyModal;
58128 this.directoryList = [];
58129 var controller = this;
58130 var directoryList = this.directoryList;
58132 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
58140 directoryList.push(o);
58142 Workspace.prototype.addDirectory = function (info, directoryList) {
58143 directoryList.push(info);
58146 Workspace.prototype.upload = function () {
58147 this.MyModal.upload();
58150 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58151 Workspace.prototype.debug = function () {
58152 this.MyModal.preview();
58154 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
58157 controllers.Workspace = Workspace;
58158 })(controllers = app.controllers || (app.controllers = {}));
58159 })(app || (app = {}));
58163 (function (controllers) {
58164 var History = (function () {
58165 function History($scope) {
58166 this.page = "History";
58168 History.$inject = ['$scope'];
58171 controllers.History = History;
58172 })(controllers = app.controllers || (app.controllers = {}));
58173 })(app || (app = {}));
58177 (function (controllers) {
58178 var SelectCommand = (function () {
58179 function SelectCommand($scope, APIEndPoint, $modalInstance) {
58180 this.APIEndPoint = APIEndPoint;
58181 this.$modalInstance = $modalInstance;
58182 var controller = this;
58185 .$promise.then(function (result) {
58186 controller.tags = result.info;
58190 .$promise.then(function (result) {
58191 controller.commands = result.info;
58193 this.currentTag = 'all';
58195 SelectCommand.prototype.changeTag = function (tag) {
58196 this.currentTag = tag;
58198 SelectCommand.prototype.selectCommand = function (command) {
58199 this.$modalInstance.close(command);
58201 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
58202 return SelectCommand;
58204 controllers.SelectCommand = SelectCommand;
58205 })(controllers = app.controllers || (app.controllers = {}));
58206 })(app || (app = {}));
58210 (function (controllers) {
58212 var Upload = (function () {
58213 function Upload($scope, APIEndPoint, $modalInstance) {
58214 this.APIEndPoint = APIEndPoint;
58215 this.$modalInstance = $modalInstance;
58216 var controller = this;
58217 console.log('controller.upload-controllers');
58219 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
58222 controllers.Upload = Upload;
58223 })(controllers = app.controllers || (app.controllers = {}));
58224 })(app || (app = {}));
58228 (function (controllers) {
58230 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58231 var Preview = (function () {
58232 function Preview($scope, APIEndPoint, $modalInstance) {
58233 this.APIEndPoint = APIEndPoint;
58234 this.$modalInstance = $modalInstance;
58235 var controller = this;
58236 console.log('preview');
58238 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
58241 controllers.Preview = Preview;
58242 })(controllers = app.controllers || (app.controllers = {}));
58243 })(app || (app = {}));
58245 (function (filters) {
58247 return function (commands, tag) {
58249 angular.forEach(commands, function (command) {
58251 angular.forEach(command.tags, function (value) {
58256 result.push(command);
58262 })(filters || (filters = {}));
58266 var appName = 'zephyr';
58267 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
58268 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
58269 $urlRouterProvider.otherwise('/execution');
58270 $locationProvider.html5Mode({
58275 .state('execution', {
58277 templateUrl: 'templates/execution.html',
58278 controller: 'executionController',
58281 .state('workspace', {
58283 templateUrl: 'templates/workspace.html',
58284 controller: 'workspaceController',
58287 .state('history', {
58289 templateUrl: 'templates/history.html',
58290 controller: 'historyController',
58294 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
58295 app.zephyr.service('MyModal', app.services.MyModal);
58296 app.zephyr.service('WebSocket', app.services.WebSocket);
58297 app.zephyr.service('Console', app.services.Console);
58298 app.zephyr.filter('Tag', filters.Tag);
58299 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
58300 app.zephyr.controller('previewController', app.controllers.Preview);
58302 app.zephyr.controller('uploadController', app.controllers.Upload);
58304 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58305 app.zephyr.controller('executionController', app.controllers.Execution);
58306 app.zephyr.controller('workspaceController', app.controllers.Workspace);
58307 app.zephyr.controller('historyController', app.controllers.History);
58308 app.zephyr.controller('commandController', app.directives.CommandController);
58309 app.zephyr.controller('optionController', app.directives.OptionController);
58310 app.zephyr.controller('directoryController', app.directives.DirectoryController);
58311 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
58313 app.zephyr.controller('uploadController', app.directives.UploadController);
58315 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58316 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
58317 app.zephyr.directive('command', app.directives.Command.Factory());
58318 app.zephyr.directive('option', app.directives.Option.Factory());
58319 app.zephyr.directive('directory', app.directives.Directory.Factory());
58320 })(app || (app = {}));
58328 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58329 /***/ function(module, exports) {
58334 (function (declares) {
58335 var CommandInfo = (function () {
58336 function CommandInfo(name) {
58339 return CommandInfo;
58341 declares.CommandInfo = CommandInfo;
58342 })(declares = app.declares || (app.declares = {}));
58343 })(app || (app = {}));
58347 (function (services) {
58348 var APIEndPoint = (function () {
58349 function APIEndPoint($resource, $http) {
58350 this.$resource = $resource;
58351 this.$http = $http;
58353 APIEndPoint.prototype.resource = function (endPoint, data) {
58354 var customAction = {
58360 headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
58362 return this.$resource(endPoint, {}, { execute: execute });
58364 APIEndPoint.prototype.getOptionControlFile = function (command) {
58365 var endPoint = '/api/v1/optionControlFile/' + command;
58366 return this.resource(endPoint, {}).get();
58368 APIEndPoint.prototype.getFiles = function (fileId) {
58369 var endPoint = '/api/v1/workspace';
58371 endPoint += '/' + fileId;
58373 return this.resource(endPoint, {}).get();
58375 APIEndPoint.prototype.getDirectories = function () {
58376 var endPoint = '/api/v1/all/workspace/directory';
58377 return this.resource(endPoint, {}).get();
58379 APIEndPoint.prototype.getTags = function () {
58380 var endPoint = '/api/v1/tagList';
58381 return this.resource(endPoint, {}).get();
58383 APIEndPoint.prototype.getCommands = function () {
58384 var endPoint = '/api/v1/commandList';
58385 return this.resource(endPoint, {}).get();
58387 APIEndPoint.prototype.execute = function (data) {
58388 var endPoint = '/api/v1/execution';
58389 var fd = new FormData();
58390 fd.append('data', data);
58391 return this.$http.post(endPoint, fd, {
58392 headers: { 'Content-Type': undefined },
58393 transformRequest: angular.identity
58396 APIEndPoint.prototype.debug = function () {
58397 var endPoint = '/api/v1/debug';
58398 return this.$http.get(endPoint);
58401 APIEndPoint.prototype.upload = function () {
58402 var endPoint = '/api/v1/upload';
58403 return this.$http.get(endPoint);
58406 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58407 APIEndPoint.prototype.help = function (command) {
58408 var endPoint = '/api/v1/help/' + command;
58409 return this.$http.get(endPoint);
58411 return APIEndPoint;
58413 services.APIEndPoint = APIEndPoint;
58414 })(services = app.services || (app.services = {}));
58415 })(app || (app = {}));
58419 (function (services) {
58420 var MyModal = (function () {
58421 function MyModal($uibModal) {
58422 this.$uibModal = $uibModal;
58423 this.modalOption = {
58430 MyModal.prototype.open = function (modalName) {
58431 if (modalName === 'SelectCommand') {
58432 this.modalOption.templateUrl = 'templates/select-command.html';
58433 this.modalOption.size = 'lg';
58435 return this.$uibModal.open(this.modalOption);
58437 MyModal.prototype.selectCommand = function () {
58438 this.modalOption.templateUrl = 'templates/select-command.html';
58439 this.modalOption.controller = 'selectCommandController';
58440 this.modalOption.controllerAs = 'c';
58441 this.modalOption.size = 'lg';
58442 return this.$uibModal.open(this.modalOption);
58444 MyModal.prototype.preview = function () {
58445 this.modalOption.templateUrl = 'templates/preview.html';
58446 this.modalOption.controller = 'previewController';
58447 this.modalOption.controllerAs = 'c';
58448 this.modalOption.size = 'lg';
58449 return this.$uibModal.open(this.modalOption);
58452 MyModal.prototype.upload = function () {
58453 this.modalOption.templateUrl = 'templates/upload.html';
58454 this.modalOption.controller = 'uploadController';
58455 this.modalOption.controllerAs = 'c';
58456 this.modalOption.size = 'lg';
58457 return this.$uibModal.open(this.modalOption);
58460 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58461 MyModal.$inject = ['$uibModal'];
58464 services.MyModal = MyModal;
58465 })(services = app.services || (app.services = {}));
58466 })(app || (app = {}));
58470 (function (services) {
58471 var WebSocket = (function () {
58472 function WebSocket($rootScope) {
58473 this.$rootScope = $rootScope;
58474 this.socket = io.connect();
58476 WebSocket.prototype.on = function (eventName, callback) {
58477 var socket = this.socket;
58478 var rootScope = this.$rootScope;
58479 socket.on(eventName, function () {
58480 var args = arguments;
58481 rootScope.$apply(function () {
58482 callback.apply(socket, args);
58486 WebSocket.prototype.emit = function (eventName, data, callback) {
58487 var socket = this.socket;
58488 var rootScope = this.$rootScope;
58489 this.socket.emit(eventName, data, function () {
58490 var args = arguments;
58491 rootScope.$apply(function () {
58493 callback.apply(socket, args);
58499 services.WebSocket = WebSocket;
58500 })(services = app.services || (app.services = {}));
58501 })(app || (app = {}));
58505 (function (services) {
58506 var Console = (function () {
58507 function Console(WebSocket, $rootScope) {
58508 this.WebSocket = WebSocket;
58509 this.$rootScope = $rootScope;
58510 this.WebSocket = WebSocket;
58511 this.$rootScope = $rootScope;
58512 this.directiveIDs = [];
58513 var directiveIDs = this.directiveIDs;
58514 this.WebSocket.on('console', function (d) {
58516 var message = d.message;
58517 if (directiveIDs.indexOf(id) > -1) {
58518 $rootScope.$emit(id, message);
58522 Console.prototype.addDirective = function (id) {
58523 if (!(this.directiveIDs.indexOf(id) > -1)) {
58524 this.directiveIDs.push(id);
58527 Console.prototype.removeDirective = function (id) {
58528 var i = this.directiveIDs.indexOf(id);
58530 this.directiveIDs.splice(i, 1);
58533 Console.prototype.showIDs = function () {
58534 console.log(this.directiveIDs);
58538 services.Console = Console;
58539 })(services = app.services || (app.services = {}));
58540 })(app || (app = {}));
58544 (function (directives) {
58545 var Command = (function () {
58546 function Command() {
58547 this.restrict = 'E';
58548 this.replace = true;
58550 this.controller = 'commandController';
58551 this.controllerAs = 'ctrl';
58552 this.bindToController = {
58558 this.templateUrl = 'templates/command.html';
58560 Command.Factory = function () {
58561 var directive = function () {
58562 return new Command();
58564 directive.$inject = [];
58569 directives.Command = Command;
58570 var CommandController = (function () {
58571 function CommandController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
58572 this.APIEndPoint = APIEndPoint;
58573 this.$scope = $scope;
58574 this.MyModal = MyModal;
58575 this.WebSocket = WebSocket;
58576 this.$window = $window;
58577 this.$rootScope = $rootScope;
58578 this.Console = Console;
58579 var controller = this;
58581 .getOptionControlFile(this.name)
58583 .then(function (result) {
58584 controller.options = result.info;
58589 .then(function (result) {
58590 controller.dirs = result.info;
58592 this.heading = "[" + this.index + "]: dcdFilePrint";
58593 this.isOpen = true;
58594 this.$scope.$on('close', function () {
58595 controller.isOpen = false;
58599 return Math.floor((1 + Math.random()) * 0x10000)
58603 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
58604 s4() + '-' + s4() + s4() + s4();
58606 this.uuid = guid();
58607 this.Console.addDirective(this.uuid);
58608 this.Console.showIDs();
58610 CommandController.prototype.submit = function () {
58612 angular.forEach(this.options, function (option) {
58614 name: option.option,
58617 angular.forEach(option.arg, function (arg) {
58619 if (typeof arg.input === 'object') {
58620 obj.arguments.push(arg.input.name);
58623 obj.arguments.push(arg.input);
58627 if (obj.arguments.length > 0) {
58632 command: this.name,
58633 workspace: this.workspace.fileId,
58637 .execute(JSON.stringify(execObj))
58638 .then(function (result) {
58639 console.log(result);
58642 CommandController.prototype.removeMySelf = function (index) {
58643 this.$scope.$destroy();
58644 this.Console.removeDirective(this.uuid);
58645 this.remove()(index, this.list);
58646 this.Console.showIDs();
58648 CommandController.prototype.reloadFiles = function () {
58650 var fileId = this.workspace.fileId;
58654 .then(function (result) {
58655 var status = result.status;
58656 if (status === 'success') {
58657 _this.files = result.info;
58660 console.log(result.message);
58664 CommandController.prototype.debug = function () {
58665 var div = angular.element(this.$window.document).find("div");
58668 angular.forEach(div, function (v) {
58669 if (v.className === "panel-body console") {
58672 else if (v.className === "row parameters-console") {
58676 var consoleHeight = parseInt(parametersTag.clientHeight.toString().replace('px', '')) - 150 + 'px';
58677 var consoleWidth = parseInt(parametersTag.clientWidth.toString().replace('px', '')) / 3 * 2 - 150 + 'px';
58678 consoleTag.style.height = consoleHeight;
58679 consoleTag.style.width = consoleWidth;
58681 CommandController.prototype.help = function () {
58684 .then(function (result) {
58685 console.log(result);
58688 CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
58689 return CommandController;
58691 directives.CommandController = CommandController;
58692 })(directives = app.directives || (app.directives = {}));
58693 })(app || (app = {}));
58697 (function (directives) {
58698 var HeaderMenu = (function () {
58699 function HeaderMenu() {
58700 this.restrict = 'E';
58701 this.replace = true;
58702 this.templateUrl = 'templates/header-menu.html';
58703 this.controller = 'HeaderMenuController';
58704 this.controllerAs = 'hmc';
58707 HeaderMenu.Factory = function () {
58708 var directive = function () {
58709 return new HeaderMenu();
58715 directives.HeaderMenu = HeaderMenu;
58716 var HeaderMenuController = (function () {
58717 function HeaderMenuController($state) {
58718 this.$state = $state;
58719 this.isExecution = this.$state.current.name === 'execution';
58720 this.isWorkspace = this.$state.current.name === 'workspace';
58721 this.isHistory = this.$state.current.name === 'history';
58723 HeaderMenuController.prototype.transit = function (state) {
58724 this.$state.go(state);
58726 HeaderMenuController.$inject = ['$state'];
58727 return HeaderMenuController;
58729 directives.HeaderMenuController = HeaderMenuController;
58730 })(directives = app.directives || (app.directives = {}));
58731 })(app || (app = {}));
58735 (function (directives) {
58736 var Option = (function () {
58737 function Option() {
58738 this.restrict = 'E';
58739 this.replace = true;
58740 this.controller = 'optionController';
58741 this.bindToController = {
58746 this.templateUrl = 'templates/option.html';
58747 this.controllerAs = 'ctrl';
58749 Option.Factory = function () {
58750 var directive = function () {
58751 return new Option();
58753 directive.$inject = [];
58758 directives.Option = Option;
58759 var OptionController = (function () {
58760 function OptionController() {
58761 var controller = this;
58762 angular.forEach(controller.info.arg, function (arg) {
58763 if (arg.initialValue) {
58764 if (arg.formType === 'number') {
58765 arg.input = parseInt(arg.initialValue);
58768 arg.input = arg.initialValue;
58773 OptionController.$inject = [];
58774 return OptionController;
58776 directives.OptionController = OptionController;
58777 })(directives = app.directives || (app.directives = {}));
58778 })(app || (app = {}));
58782 (function (directives) {
58783 var Directory = (function () {
58784 function Directory() {
58785 this.restrict = 'E';
58786 this.replace = true;
58787 this.controller = 'directoryController';
58788 this.controllerAs = 'ctrl';
58789 this.bindToController = {
58795 this.templateUrl = 'templates/directory.html';
58797 Directory.Factory = function () {
58798 var directive = function () {
58799 return new Directory();
58805 directives.Directory = Directory;
58806 var DirectoryController = (function () {
58807 function DirectoryController(APIEndPoint, $scope) {
58808 this.APIEndPoint = APIEndPoint;
58809 this.$scope = $scope;
58810 var controller = this;
58812 .getFiles(this.info.fileId)
58814 .then(function (result) {
58815 if (result.status === 'success') {
58816 controller.files = result.info;
58817 angular.forEach(result.info, function (file) {
58818 if (file.fileType === '0') {
58820 if (controller.info.path === '/') {
58821 o.path = '/' + file.name;
58824 o.path = controller.info.path + '/' + file.name;
58826 controller.add()(o, controller.list);
58833 DirectoryController.$inject = ['APIEndPoint', '$scope'];
58834 return DirectoryController;
58836 directives.DirectoryController = DirectoryController;
58837 })(directives = app.directives || (app.directives = {}));
58838 })(app || (app = {}));
58843 (function (directives) {
58844 var Upload = (function () {
58845 function Upload() {
58846 this.restrict = 'E';
58847 this.replace = true;
58849 this.controller = 'UploadController';
58850 this.controllerAs = 'ctrl';
58851 this.bindToController = {
58857 this.templateUrl = 'templates/upload.html';
58858 console.log("templates/upload.html-constructor");
58860 Upload.Factory = function () {
58861 var directive = function () {
58862 return new Upload();
58864 directive.$inject = [];
58869 directives.Upload = Upload;
58870 var UploadController = (function () {
58871 function UploadController(APIEndPoint, $scope, MyModal, WebSocket, $window, $rootScope, Console) {
58872 this.APIEndPoint = APIEndPoint;
58873 this.$scope = $scope;
58874 this.MyModal = MyModal;
58875 this.WebSocket = WebSocket;
58876 this.$window = $window;
58877 this.$rootScope = $rootScope;
58878 this.Console = Console;
58879 var controller = this;
58880 console.log("directive.upload-constructor");
58882 UploadController.prototype.submit = function () {
58883 console.log("submit: function not supported¥n");
58885 UploadController.$inject = ['APIEndPoint', '$scope', 'MyModal', 'WebSocket', '$window', '$rootScope', 'Console'];
58886 return UploadController;
58888 directives.UploadController = UploadController;
58889 })(directives = app.directives || (app.directives = {}));
58890 })(app || (app = {}));
58894 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58896 (function (controllers) {
58897 var Execution = (function () {
58898 function Execution(MyModal, $scope) {
58899 this.MyModal = MyModal;
58900 this.$scope = $scope;
58901 this.commandInfoList = [];
58904 Execution.prototype.add = function () {
58905 this.$scope.$broadcast('close');
58906 var commandInfoList = this.commandInfoList;
58907 var commandInstance = this.MyModal.selectCommand();
58910 .then(function (command) {
58911 commandInfoList.push(new app.declares.CommandInfo(command));
58914 Execution.prototype.open = function () {
58915 var result = this.MyModal.open('SelectCommand');
58916 console.log(result);
58918 Execution.prototype.remove = function (index, list) {
58919 list.splice(index, 1);
58921 Execution.prototype.close = function () {
58922 console.log("close");
58924 Execution.$inject = ['MyModal', '$scope'];
58927 controllers.Execution = Execution;
58928 })(controllers = app.controllers || (app.controllers = {}));
58929 })(app || (app = {}));
58933 (function (controllers) {
58934 var Workspace = (function () {
58935 function Workspace($scope, APIEndPoint, MyModal) {
58936 this.$scope = $scope;
58937 this.APIEndPoint = APIEndPoint;
58938 this.MyModal = MyModal;
58939 this.directoryList = [];
58940 var controller = this;
58941 var directoryList = this.directoryList;
58943 fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
58951 directoryList.push(o);
58953 Workspace.prototype.addDirectory = function (info, directoryList) {
58954 directoryList.push(info);
58957 Workspace.prototype.upload = function () {
58958 this.MyModal.upload();
58961 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
58962 Workspace.prototype.debug = function () {
58963 this.MyModal.preview();
58965 Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
58968 controllers.Workspace = Workspace;
58969 })(controllers = app.controllers || (app.controllers = {}));
58970 })(app || (app = {}));
58974 (function (controllers) {
58975 var History = (function () {
58976 function History($scope) {
58977 this.page = "History";
58979 History.$inject = ['$scope'];
58982 controllers.History = History;
58983 })(controllers = app.controllers || (app.controllers = {}));
58984 })(app || (app = {}));
58988 (function (controllers) {
58989 var SelectCommand = (function () {
58990 function SelectCommand($scope, APIEndPoint, $modalInstance) {
58991 this.APIEndPoint = APIEndPoint;
58992 this.$modalInstance = $modalInstance;
58993 var controller = this;
58996 .$promise.then(function (result) {
58997 controller.tags = result.info;
59001 .$promise.then(function (result) {
59002 controller.commands = result.info;
59004 this.currentTag = 'all';
59006 SelectCommand.prototype.changeTag = function (tag) {
59007 this.currentTag = tag;
59009 SelectCommand.prototype.selectCommand = function (command) {
59010 this.$modalInstance.close(command);
59012 SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
59013 return SelectCommand;
59015 controllers.SelectCommand = SelectCommand;
59016 })(controllers = app.controllers || (app.controllers = {}));
59017 })(app || (app = {}));
59021 (function (controllers) {
59023 var Upload = (function () {
59024 function Upload($scope, APIEndPoint, $modalInstance) {
59025 this.APIEndPoint = APIEndPoint;
59026 this.$modalInstance = $modalInstance;
59027 var controller = this;
59028 console.log('controller.upload-controllers');
59030 Upload.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
59033 controllers.Upload = Upload;
59034 })(controllers = app.controllers || (app.controllers = {}));
59035 })(app || (app = {}));
59039 (function (controllers) {
59041 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
59042 var Preview = (function () {
59043 function Preview($scope, APIEndPoint, $modalInstance) {
59044 this.APIEndPoint = APIEndPoint;
59045 this.$modalInstance = $modalInstance;
59046 var controller = this;
59047 console.log('preview');
59049 Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
59052 controllers.Preview = Preview;
59053 })(controllers = app.controllers || (app.controllers = {}));
59054 })(app || (app = {}));
59056 (function (filters) {
59058 return function (commands, tag) {
59060 angular.forEach(commands, function (command) {
59062 angular.forEach(command.tags, function (value) {
59067 result.push(command);
59073 })(filters || (filters = {}));
59077 var appName = 'zephyr';
59078 app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
59079 app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
59080 $urlRouterProvider.otherwise('/execution');
59081 $locationProvider.html5Mode({
59086 .state('execution', {
59088 templateUrl: 'templates/execution.html',
59089 controller: 'executionController',
59092 .state('workspace', {
59094 templateUrl: 'templates/workspace.html',
59095 controller: 'workspaceController',
59098 .state('history', {
59100 templateUrl: 'templates/history.html',
59101 controller: 'historyController',
59105 app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
59106 app.zephyr.service('MyModal', app.services.MyModal);
59107 app.zephyr.service('WebSocket', app.services.WebSocket);
59108 app.zephyr.service('Console', app.services.Console);
59109 app.zephyr.filter('Tag', filters.Tag);
59110 app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
59111 app.zephyr.controller('previewController', app.controllers.Preview);
59113 app.zephyr.controller('uploadController', app.controllers.Upload);
59115 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
59116 app.zephyr.controller('executionController', app.controllers.Execution);
59117 app.zephyr.controller('workspaceController', app.controllers.Workspace);
59118 app.zephyr.controller('historyController', app.controllers.History);
59119 app.zephyr.controller('commandController', app.directives.CommandController);
59120 app.zephyr.controller('optionController', app.directives.OptionController);
59121 app.zephyr.controller('directoryController', app.directives.DirectoryController);
59122 app.zephyr.controller('HeaderMenuController', app.directives.HeaderMenuController);
59124 app.zephyr.controller('uploadController', app.directives.UploadController);
59126 >>>>>>> 6b2b2b88511733893d2c6e7848c389abfcd53ba6
59127 app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
59128 app.zephyr.directive('command', app.directives.Command.Factory());
59129 app.zephyr.directive('option', app.directives.Option.Factory());
59130 app.zephyr.directive('directory', app.directives.Directory.Factory());
59131 })(app || (app = {}));