'use strict'; ////////////////////////////////// //JQLite ////////////////////////////////// /** * @workInProgress * @ngdoc function * @name angular.element * @function * * @description * Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element. * `angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if * jQuery is loaded or a function that wraps the element or string in angular's jQuery lite * implementation. * * Real jQuery always takes precedence (as long as it was loaded before `DOMContentEvent`) * * Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows * angular to manipulate DOM. The jQuery lite implements only a subset of jQuery api, with the * focus on the most commonly needed functionality and minimal footprint. For this reason only a * limited number of jQuery methods, arguments and invocation styles are supported. * * Note: All element references in angular are always wrapped with jQuery (lite) and are never * raw DOM references. * * ## Angular's jQuery lite implements these functions: * * - [addClass()](http://api.jquery.com/addClass/) * - [after()](http://api.jquery.com/after/) * - [append()](http://api.jquery.com/append/) * - [attr()](http://api.jquery.com/attr/) * - [bind()](http://api.jquery.com/bind/) * - [children()](http://api.jquery.com/children/) * - [clone()](http://api.jquery.com/clone/) * - [css()](http://api.jquery.com/css/) * - [data()](http://api.jquery.com/data/) * - [hasClass()](http://api.jquery.com/hasClass/) * - [parent()](http://api.jquery.com/parent/) * - [remove()](http://api.jquery.com/remove/) * - [removeAttr()](http://api.jquery.com/removeAttr/) * - [removeClass()](http://api.jquery.com/removeClass/) * - [removeData()](http://api.jquery.com/removeData/) * - [replaceWith()](http://api.jquery.com/replaceWith/) * - [text()](http://api.jquery.com/text/) * - [trigger()](http://api.jquery.com/trigger/) * - [eq()](http://api.jquery.com/eq/) * * ## Additionally these methods extend the jQuery and are available in both jQuery and jQuery lite * version: * *- `scope()` - retrieves the current angular scope of the element. * * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. * @returns {Object} jQuery object. */ var jqCache = {}, jqName = 'ng-' + new Date().getTime(), jqId = 1, addEventListenerFn = (window.document.addEventListener ? function(element, type, fn) {element.addEventListener(type, fn, false);} : function(element, type, fn) {element.attachEvent('on' + type, fn);}), removeEventListenerFn = (window.document.removeEventListener ? function(element, type, fn) {element.removeEventListener(type, fn, false); } : function(element, type, fn) {element.detachEvent('on' + type, fn); }); function jqNextId() { return (jqId++); } function getStyle(element) { var current = {}, style = element[0].style, value, name, i; if (typeof style.length == 'number') { for(i = 0; i < style.length; i++) { name = style[i]; current[name] = style[name]; } } else { for (name in style) { value = style[name]; if (1*name != name && name != 'cssText' && value && typeof value == 'string' && value !='false') current[name] = value; } } return current; } ///////////////////////////////////////////// function jqLiteWrap(element) { if (isString(element) && element.charAt(0) != '<') { throw new Error('selectors not implemented'); } return new JQLite(element); } function JQLite(element) { if (element instanceof JQLite) { return element; } else if (isString(element)) { var div = document.createElement('div'); // Read about the NoScope elements here: // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx div.innerHTML = '
 
' + element; // IE insanity to make NoScope elements work! div.removeChild(div.firstChild); // remove the superfluous div JQLiteAddNodes(this, div.childNodes); this.remove(); // detach the elements from the temporary DOM div. } else { JQLiteAddNodes(this, element); } } function JQLiteClone(element) { return element.cloneNode(true); } function JQLiteDealoc(element){ JQLiteRemoveData(element); for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { JQLiteDealoc(children[i]); } } function JQLiteRemoveData(element) { var cacheId = element[jqName], cache = jqCache[cacheId]; if (cache) { forEach(cache.bind || {}, function(fn, type){ removeEventListenerFn(element, type, fn); }); delete jqCache[cacheId]; element[jqName] = undefined; // ie does not allow deletion of attributes on elements. } } function JQLiteData(element, key, value) { var cacheId = element[jqName], cache = jqCache[cacheId || -1]; if (isDefined(value)) { if (!cache) { element[jqName] = cacheId = jqNextId(); cache = jqCache[cacheId] = {}; } cache[key] = value; } else { return cache ? cache[key] : null; } } function JQLiteHasClass(element, selector, _) { // the argument '_' is important, since it makes the function have 3 arguments, which // is needed for delegate function to realize the this is a getter. var className = " " + selector + " "; return ((" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1); } function JQLiteRemoveClass(element, selector) { element.className = trim( (" " + element.className + " ") .replace(/[\n\t]/g, " ") .replace(" " + selector + " ", "") ); } function JQLiteAddClass(element, selector ) { if (!JQLiteHasClass(element, selector)) { element.className = trim(element.className + ' ' + selector); } } function JQLiteAddNodes(root, elements) { if (elements) { elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) ? elements : [ elements ]; for(var i=0; i < elements.length; i++) { root.push(elements[i]); } } } ////////////////////////////////////////// // Functions which are declared directly. ////////////////////////////////////////// var JQLitePrototype = JQLite.prototype = { ready: function(fn) { var fired = false; function trigger() { if (fired) return; fired = true; fn(); } this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 // we can not use jqLite since we are not done loading and jQuery could be loaded later. jqLiteWrap(window).bind('load', trigger); // fallback to window.onload for others }, toString: function() { var value = []; forEach(this, function(e){ value.push('' + e);}); return '[' + value.join(', ') + ']'; }, eq: function(index) { return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); }, length: 0, push: push, sort: [].sort, splice: [].splice }; ////////////////////////////////////////// // Functions iterating getter/setters. // these functions return self on setter and // value on get. ////////////////////////////////////////// var SPECIAL_ATTR = makeMap("multiple,selected,checked,disabled,readonly"); forEach({ data: JQLiteData, scope: function(element) { var scope; while (element && !(scope = jqLite(element).data($$scope))) { element = element.parentNode; } return scope; }, removeAttr: function(element,name) { element.removeAttribute(name); }, hasClass: JQLiteHasClass, css: function(element, name, value) { if (isDefined(value)) { element.style[name] = value; } else { return element.style[name]; } }, attr: function(element, name, value){ if (SPECIAL_ATTR[name]) { if (isDefined(value)) { element[name] = !!value; } else { return element[name]; } } else if (isDefined(value)) { element.setAttribute(name, value); } else if (element.getAttribute) { // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code // some elements (e.g. Document) don't have get attribute, so return undefined var ret = element.getAttribute(name, 2); // normalize non-existing attributes to undefined (as jQuery) return ret === null ? undefined : ret; } }, text: extend((msie < 9) ? function(element, value) { // NodeType == 3 is text node if (element.nodeType == 3) { if (isUndefined(value)) return element.nodeValue; element.nodeValue = value; } else { if (isUndefined(value)) return element.innerText; element.innerText = value; } } : function(element, value) { if (isUndefined(value)) { return element.textContent; } element.textContent = value; }, {$dv:''}), val: function(element, value) { if (isUndefined(value)) { return element.value; } element.value = value; }, html: function(element, value) { if (isUndefined(value)) { return element.innerHTML; } for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { JQLiteDealoc(childNodes[i]); } element.innerHTML = value; } }, function(fn, name){ /** * Properties: writes return selection, reads return first value */ JQLite.prototype[name] = function(arg1, arg2) { var i, key; if ((fn.length == 2 ? arg1 : arg2) === undefined) { if (isObject(arg1)) { // we are a write, but the object properties are the key/values for(i=0; i < this.length; i++) { for (key in arg1) { fn(this[i], key, arg1[key]); } } // return self for chaining return this; } else { // we are a read, so read the first child. if (this.length) return fn(this[0], arg1, arg2); } } else { // we are a write, so apply to all children for(i=0; i < this.length; i++) { fn(this[i], arg1, arg2); } // return self for chaining return this; } return fn.$dv; }; }); ////////////////////////////////////////// // Functions iterating traversal. // These functions chain results into a single // selector. ////////////////////////////////////////// forEach({ removeData: JQLiteRemoveData, dealoc: JQLiteDealoc, bind: function(element, type, fn){ var bind = JQLiteData(element, 'bind'); if (!bind) JQLiteData(element, 'bind', bind = {}); forEach(type.split(' '), function(type){ var eventHandler = bind[type]; if (!eventHandler) { bind[type] = eventHandler = function(event) { if (!event.preventDefault) { event.preventDefault = function(){ event.returnValue = false; //ie }; } if (!event.stopPropagation) { event.stopPropagation = function() { event.cancelBubble = true; //ie }; } if (!event.target) { event.target = event.srcElement || document; } forEach(eventHandler.fns, function(fn){ fn.call(element, event); }); }; eventHandler.fns = []; addEventListenerFn(element, type, eventHandler); } eventHandler.fns.push(fn); }); }, replaceWith: function(element, replaceNode) { var index, parent = element.parentNode; JQLiteDealoc(element); forEach(new JQLite(replaceNode), function(node){ if (index) { parent.insertBefore(node, index.nextSibling); } else { parent.replaceChild(node, element); } index = node; }); }, children: function(element) { var children = []; forEach(element.childNodes, function(element){ if (element.nodeName != '#text') children.push(element); }); return children; }, append: function(element, node) { forEach(new JQLite(node), function(child){ if (element.nodeType === 1) element.appendChild(child); }); }, prepend: function(element, node) { if (element.nodeType === 1) { var index = element.firstChild; forEach(new JQLite(node), function(child){ if (index) { element.insertBefore(child, index); } else { element.appendChild(child); index = child; } }); } }, remove: function(element) { JQLiteDealoc(element); var parent = element.parentNode; if (parent) parent.removeChild(element); }, after: function(element, newElement) { var index = element, parent = element.parentNode; forEach(new JQLite(newElement), function(node){ parent.insertBefore(node, index.nextSibling); index = node; }); }, addClass: JQLiteAddClass, removeClass: JQLiteRemoveClass, toggleClass: function(element, selector, condition) { if (isUndefined(condition)) { condition = !JQLiteHasClass(element, selector); } (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); }, parent: function(element) { var parent = element.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, next: function(element) { return element.nextSibling; }, find: function(element, selector) { return element.getElementsByTagName(selector); }, clone: JQLiteClone }, function(fn, name){ /** * chaining functions */ JQLite.prototype[name] = function(arg1, arg2) { var value; for(var i=0; i < this.length; i++) { if (value == undefined) { value = fn(this[i], arg1, arg2); if (value !== undefined) { // any function which returns a value needs to be wrapped value = jqLite(value); } } else { JQLiteAddNodes(value, fn(this[i], arg1, arg2)); } } return value == undefined ? this : value; }; });