DVHMA/DVHMA-OpenUI5/www/resources/sap-ui-core-dbg.js

51330 lines
1.6 MiB
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* jQuery JavaScript Library v1.11.1
* http://jquery.com/
*
* Includes Sizzle.js
* http://sizzlejs.com/
*
* Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-05-01T17:42Z
*/
(function( global, factory ) {
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper window is present,
// execute the factory and get jQuery
// For environments that do not inherently posses a window with a document
// (such as Node.js), expose a jQuery-making factory as module.exports
// This accentuates the need for the creation of a real window
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
// Can't do this because several apps including ASP.NET trace
// the stack via arguments.caller.callee and Firefox dies if
// you try to trace through "use strict" call chains. (#13335)
// Support: Firefox 18+
//
var deletedIds = [];
var slice = deletedIds.slice;
var concat = deletedIds.concat;
var push = deletedIds.push;
var indexOf = deletedIds.indexOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var support = {};
var
version = "1.11.1",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
// Support: Android<4.1, IE<9
// Make sure we trim BOM and NBSP
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
// Matches dashed string for camelizing
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi,
// Used by jQuery.camelCase as callback to replace()
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version,
constructor: jQuery,
// Start with an empty selector
selector: "",
// The default length of a jQuery object is 0
length: 0,
toArray: function() {
return slice.call( this );
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num != null ?
// Return just the one element from the set
( num < 0 ? this[ num + this.length ] : this[ num ] ) :
// Return all the elements in a clean array
slice.call( this );
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
// Return the newly-formed element set
return ret;
},
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
},
first: function() {
return this.eq( 0 );
},
last: function() {
return this.eq( -1 );
},
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
},
end: function() {
return this.prevObject || this.constructor(null);
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: deletedIds.sort,
splice: deletedIds.splice
};
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
jQuery.extend({
// Unique for each copy of jQuery on the page
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
// Assume jQuery is ready without the ready module
isReady: true,
error: function( msg ) {
throw new Error( msg );
},
noop: function() {},
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
},
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
isWindow: function( obj ) {
/* jshint eqeqeq: false */
return obj != null && obj == obj.window;
},
isNumeric: function( obj ) {
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
// subtraction forces infinities to NaN
return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
},
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
isPlainObject: function( obj ) {
var key;
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
try {
// Not own constructor property must be Object
if ( obj.constructor &&
!hasOwn.call(obj, "constructor") &&
!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// Support: IE<9
// Handle iteration over inherited properties before own properties.
if ( support.ownLast ) {
for ( key in obj ) {
return hasOwn.call( obj, key );
}
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
for ( key in obj ) {}
return key === undefined || hasOwn.call( obj, key );
},
type: function( obj ) {
if ( obj == null ) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call(obj) ] || "object" :
typeof obj;
},
// Evaluates a script in a global context
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
// Convert dashed to camelCase; used by the css and data modules
// Microsoft forgot to hump their vendor prefix (#9572)
camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
// args is for internal usage only
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
}
// A special, fast, case for the most common use of each
} else {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
}
return obj;
},
// Support: Android<4.1, IE<9
trim: function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
},
// results is for internal usage only
makeArray: function( arr, results ) {
var ret = results || [];
if ( arr != null ) {
if ( isArraylike( Object(arr) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
push.call( ret, arr );
}
}
return ret;
},
inArray: function( elem, arr, i ) {
var len;
if ( arr ) {
if ( indexOf ) {
return indexOf.call( arr, elem, i );
}
len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
}
return -1;
},
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
while ( j < len ) {
first[ i++ ] = second[ j++ ];
}
// Support: IE<9
// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
if ( len !== len ) {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
grep: function( elems, callback, invert ) {
var callbackInverse,
matches = [],
i = 0,
length = elems.length,
callbackExpect = !invert;
// Go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
callbackInverse = !callback( elems[ i ], i );
if ( callbackInverse !== callbackExpect ) {
matches.push( elems[ i ] );
}
}
return matches;
},
// arg is for internal usage only
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// Go through the array, translating each of the items to their new values
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// Flatten any nested arrays
return concat.apply( [], ret );
},
// A global GUID counter for objects
guid: 1,
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
var args, proxy, tmp;
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// Simulated bind
args = slice.call( arguments, 2 );
proxy = function() {
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
},
now: function() {
return +( new Date() );
},
// jQuery.support is not used in Core but other projects attach their
// properties to it so it needs to exist.
support: support
});
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
function isArraylike( obj ) {
var length = obj.length,
type = jQuery.type( obj );
if ( type === "function" || jQuery.isWindow( obj ) ) {
return false;
}
if ( obj.nodeType === 1 && length ) {
return true;
}
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
var Sizzle =
/*!
* Sizzle CSS Selector Engine v1.10.19
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-04-18
*/
(function( window ) {
var i,
support,
Expr,
getText,
isXML,
tokenize,
compile,
select,
outermostContext,
sortInput,
hasDuplicate,
// Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains,
// Instance-specific data
expando = "sizzle" + -(new Date()),
preferredDoc = window.document,
dirruns = 0,
done = 0,
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
}
return 0;
},
// General-purpose constants
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31,
// Instance methods
hasOwn = ({}).hasOwnProperty,
arr = [],
pop = arr.pop,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one
indexOf = arr.indexOf || function( elem ) {
var i = 0,
len = this.length;
for ( ; i < len; i++ ) {
if ( this[i] === elem ) {
return i;
}
}
return -1;
},
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
// Regular expressions
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// http://www.w3.org/TR/css3-syntax/#characters
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
// Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace( "w", "w#" ),
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
// Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
"*\\]",
pseudos = ":(" + characterEncoding + ")(?:\\((" +
// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
// 1. quoted (capture 3; capture 4 or capture 5)
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
// 2. simple (capture 6)
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
// 3. anything else (capture 2)
".*" +
")\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ),
matchExpr = {
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i,
rnative = /^[^{]+\{\s*\[native \w/,
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
rsibling = /[+~]/,
rescape = /'|\\/g,
// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
funescape = function( _, escaped, escapedWhitespace ) {
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox<24
// Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ?
escaped :
high < 0 ?
// BMP codepoint
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};
// Optimize for push.apply( _, NodeList )
try {
push.apply(
(arr = slice.call( preferredDoc.childNodes )),
preferredDoc.childNodes
);
// Support: Android<4.0
// Detect silently failing push.apply
arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
push = { apply: arr.length ?
// Leverage slice if possible
function( target, els ) {
push_native.apply( target, slice.call(els) );
} :
// Support: IE<9
// Otherwise append directly
function( target, els ) {
var j = target.length,
i = 0;
// Can't trust NodeList.length
while ( (target[j++] = els[i++]) ) {}
target.length = j - 1;
}
};
}
function Sizzle( selector, context, results, seed ) {
var match, elem, m, nodeType,
// QSA vars
i, groups, old, nid, newContext, newSelector;
if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
setDocument( context );
}
context = context || document;
results = results || [];
if ( !selector || typeof selector !== "string" ) {
return results;
}
if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
return [];
}
if ( documentIsHTML && !seed ) {
// Shortcuts
if ( (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document (jQuery #6963)
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
}
// Speed-up: Sizzle("TAG")
} else if ( match[2] ) {
push.apply( results, context.getElementsByTagName( selector ) );
return results;
// Speed-up: Sizzle(".CLASS")
} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
}
// QSA path
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
nid = old = expando;
newContext = context;
newSelector = nodeType === 9 && selector;
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) {
nid = old.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
nid = "[id='" + nid + "'] ";
i = groups.length;
while ( i-- ) {
groups[i] = nid + toSelector( groups[i] );
}
newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
newSelector = groups.join(",");
}
if ( newSelector ) {
try {
push.apply( results,
newContext.querySelectorAll( newSelector )
);
return results;
} catch(qsaError) {
} finally {
if ( !old ) {
context.removeAttribute("id");
}
}
}
}
}
// All others
return select( selector.replace( rtrim, "$1" ), context, results, seed );
}
/**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function createCache() {
var keys = [];
function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if ( keys.push( key + " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
return (cache[ key + " " ] = value);
}
return cache;
}
/**
* Mark a function for special use by Sizzle
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
fn[ expando ] = true;
return fn;
}
/**
* Support testing using an element
* @param {Function} fn Passed the created div and expects a boolean result
*/
function assert( fn ) {
var div = document.createElement("div");
try {
return !!fn( div );
} catch (e) {
return false;
} finally {
// Remove from its parent by default
if ( div.parentNode ) {
div.parentNode.removeChild( div );
}
// release memory in IE
div = null;
}
}
/**
* Adds the same handler for all of the specified attrs
* @param {String} attrs Pipe-separated list of attributes
* @param {Function} handler The method that will be applied
*/
function addHandle( attrs, handler ) {
var arr = attrs.split("|"),
i = attrs.length;
while ( i-- ) {
Expr.attrHandle[ arr[i] ] = handler;
}
}
/**
* Checks document order of two siblings
* @param {Element} a
* @param {Element} b
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
*/
function siblingCheck( a, b ) {
var cur = b && a,
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
( ~b.sourceIndex || MAX_NEGATIVE ) -
( ~a.sourceIndex || MAX_NEGATIVE );
// Use IE sourceIndex if available on both nodes
if ( diff ) {
return diff;
}
// Check if b follows a
if ( cur ) {
while ( (cur = cur.nextSibling) ) {
if ( cur === b ) {
return -1;
}
}
}
return a ? 1 : -1;
}
/**
* Returns a function to use in pseudos for input types
* @param {String} type
*/
function createInputPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for buttons
* @param {String} type
*/
function createButtonPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for positionals
* @param {Function} fn
*/
function createPositionalPseudo( fn ) {
return markFunction(function( argument ) {
argument = +argument;
return markFunction(function( seed, matches ) {
var j,
matchIndexes = fn( [], seed.length, argument ),
i = matchIndexes.length;
// Match elements found at the specified indexes
while ( i-- ) {
if ( seed[ (j = matchIndexes[i]) ] ) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
}
/**
* Checks a node for validity as a Sizzle context
* @param {Element|Object=} context
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
*/
function testContext( context ) {
return context && typeof context.getElementsByTagName !== strundefined && context;
}
// Expose support vars for convenience
support = Sizzle.support = {};
/**
* Detects XML nodes
* @param {Element|Object} elem An element or a document
* @returns {Boolean} True iff elem is a non-HTML XML node
*/
isXML = Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [doc] An element or document object to use to set the document
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
var hasCompare,
doc = node ? node.ownerDocument || node : preferredDoc,
parent = doc.defaultView;
// If no document and documentElement is available, return
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
return document;
}
// Set our document
document = doc;
docElem = doc.documentElement;
// Support tests
documentIsHTML = !isXML( doc );
// Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
// IE6-8 do not support the defaultView property so parent will be undefined
if ( parent && parent !== parent.top ) {
// IE11 does not have attachEvent, so all must suffer
if ( parent.addEventListener ) {
parent.addEventListener( "unload", function() {
setDocument();
}, false );
} else if ( parent.attachEvent ) {
parent.attachEvent( "onunload", function() {
setDocument();
});
}
}
/* Attributes
---------------------------------------------------------------------- */
// Support: IE<8
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
support.attributes = assert(function( div ) {
div.className = "i";
return !div.getAttribute("className");
});
/* getElement(s)By*
---------------------------------------------------------------------- */
// Check if getElementsByTagName("*") returns only elements
support.getElementsByTagName = assert(function( div ) {
div.appendChild( doc.createComment("") );
return !div.getElementsByTagName("*").length;
});
// Check if getElementsByClassName can be trusted
support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
div.innerHTML = "<div class='a'></div><div class='a i'></div>";
// Support: Safari<4
// Catch class over-caching
div.firstChild.className = "i";
// Support: Opera<10
// Catch gEBCN failure to find non-leading classes
return div.getElementsByClassName("i").length === 2;
});
// Support: IE<10
// Check if getElementById returns elements by name
// The broken getElementById methods don't pick up programatically-set names,
// so use a roundabout getElementsByName test
support.getById = assert(function( div ) {
docElem.appendChild( div ).id = expando;
return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
});
// ID find and filter
if ( support.getById ) {
Expr.find["ID"] = function( id, context ) {
if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [ m ] : [];
}
};
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
return elem.getAttribute("id") === attrId;
};
};
} else {
// Support: IE6/7
// getElementById is not reliable as a find shortcut
delete Expr.find["ID"];
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
}
// Tag
Expr.find["TAG"] = support.getElementsByTagName ?
function( tag, context ) {
if ( typeof context.getElementsByTagName !== strundefined ) {
return context.getElementsByTagName( tag );
}
} :
function( tag, context ) {
var elem,
tmp = [],
i = 0,
results = context.getElementsByTagName( tag );
// Filter out possible comments
if ( tag === "*" ) {
while ( (elem = results[i++]) ) {
if ( elem.nodeType === 1 ) {
tmp.push( elem );
}
}
return tmp;
}
return results;
};
// Class
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
return context.getElementsByClassName( className );
}
};
/* QSA/matchesSelector
---------------------------------------------------------------------- */
// QSA and matchesSelector support
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
rbuggyMatches = [];
// qSa(:focus) reports false when true (Chrome 21)
// We allow this because of a bug in IE8/9 that throws an error
// whenever `document.activeElement` is accessed on an iframe
// So, we allow :focus to pass through QSA all the time to avoid the IE error
// See http://bugs.jquery.com/ticket/13378
rbuggyQSA = [];
if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert(function( div ) {
// Select is set to empty string on purpose
// This is to test IE's treatment of not explicitly
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
// Support: IE8, Opera 11-12.16
// Nothing should be selected when empty strings follow ^= or $= or *=
// The test attribute must be unknown in Opera but "safe" for WinRT
// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
if ( div.querySelectorAll("[msallowclip^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
}
// Support: IE8
// Boolean attributes and "value" are not treated correctly
if ( !div.querySelectorAll("[selected]").length ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
}
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked");
}
});
assert(function( div ) {
// Support: Windows 8 Native Apps
// The type and name attributes are restricted during .innerHTML assignment
var input = doc.createElement("input");
input.setAttribute( "type", "hidden" );
div.appendChild( input ).setAttribute( "name", "D" );
// Support: IE8
// Enforce case-sensitivity of name attribute
if ( div.querySelectorAll("[name=d]").length ) {
rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
}
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":enabled").length ) {
rbuggyQSA.push( ":enabled", ":disabled" );
}
// Opera 10-11 does not throw on post-comma invalid pseudos
div.querySelectorAll("*,:x");
rbuggyQSA.push(",.*:");
});
}
if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector) )) ) {
assert(function( div ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
support.disconnectedMatch = matches.call( div, "div" );
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call( div, "[s!='']:x" );
rbuggyMatches.push( "!=", pseudos );
});
}
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
/* Contains
---------------------------------------------------------------------- */
hasCompare = rnative.test( docElem.compareDocumentPosition );
// Element contains another
// Purposefully does not implement inclusive descendent
// As in, an element does not contain itself
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
));
} :
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
return false;
};
/* Sorting
---------------------------------------------------------------------- */
// Document order sorting
sortOrder = hasCompare ?
function( a, b ) {
// Flag for duplicate removal
if ( a === b ) {
hasDuplicate = true;
return 0;
}
// Sort on method existence if only one input has compareDocumentPosition
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
if ( compare ) {
return compare;
}
// Calculate position if both inputs belong to the same document
compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
a.compareDocumentPosition( b ) :
// Otherwise we know they are disconnected
1;
// Disconnected nodes
if ( compare & 1 ||
(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
// Choose the first element that is related to our preferred document
if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
return -1;
}
if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
return 1;
}
// Maintain original order
return sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
}
return compare & 4 ? -1 : 1;
} :
function( a, b ) {
// Exit early if the nodes are identical
if ( a === b ) {
hasDuplicate = true;
return 0;
}
var cur,
i = 0,
aup = a.parentNode,
bup = b.parentNode,
ap = [ a ],
bp = [ b ];
// Parentless nodes are either documents or disconnected
if ( !aup || !bup ) {
return a === doc ? -1 :
b === doc ? 1 :
aup ? -1 :
bup ? 1 :
sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
// If the nodes are siblings, we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
}
// Otherwise we need full lists of their ancestors for comparison
cur = a;
while ( (cur = cur.parentNode) ) {
ap.unshift( cur );
}
cur = b;
while ( (cur = cur.parentNode) ) {
bp.unshift( cur );
}
// Walk down the tree looking for a discrepancy
while ( ap[i] === bp[i] ) {
i++;
}
return i ?
// Do a sibling check if the nodes have a common ancestor
siblingCheck( ap[i], bp[i] ) :
// Otherwise nodes in our document sort first
ap[i] === preferredDoc ? -1 :
bp[i] === preferredDoc ? 1 :
0;
};
return doc;
};
Sizzle.matches = function( expr, elements ) {
return Sizzle( expr, null, null, elements );
};
Sizzle.matchesSelector = function( elem, expr ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
}
// Make sure that attribute selectors are quoted
expr = expr.replace( rattributeQuotes, "='$1']" );
if ( support.matchesSelector && documentIsHTML &&
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
try {
var ret = matches.call( elem, expr );
// IE 9's matchesSelector returns false on disconnected nodes
if ( ret || support.disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
} catch(e) {}
}
return Sizzle( expr, document, null, [ elem ] ).length > 0;
};
Sizzle.contains = function( context, elem ) {
// Set document vars if needed
if ( ( context.ownerDocument || context ) !== document ) {
setDocument( context );
}
return contains( context, elem );
};
Sizzle.attr = function( elem, name ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
}
var fn = Expr.attrHandle[ name.toLowerCase() ],
// Don't get fooled by Object.prototype properties (jQuery #13807)
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
fn( elem, name, !documentIsHTML ) :
undefined;
return val !== undefined ?
val :
support.attributes || !documentIsHTML ?
elem.getAttribute( name ) :
(val = elem.getAttributeNode(name)) && val.specified ?
val.value :
null;
};
Sizzle.error = function( msg ) {
throw new Error( "Syntax error, unrecognized expression: " + msg );
};
/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function( results ) {
var elem,
duplicates = [],
j = 0,
i = 0;
// Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice( 0 );
results.sort( sortOrder );
if ( hasDuplicate ) {
while ( (elem = results[i++]) ) {
if ( elem === results[ i ] ) {
j = duplicates.push( i );
}
}
while ( j-- ) {
results.splice( duplicates[ j ], 1 );
}
}
// Clear input after sorting to release objects
// See https://github.com/jquery/sizzle/pull/225
sortInput = null;
return results;
};
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
while ( (node = elem[i++]) ) {
// Do not traverse comment nodes
ret += getText( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (jQuery #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
Expr = Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
preFilter: {
"ATTR": function( match ) {
match[1] = match[1].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted
match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
if ( match[2] === "~=" ) {
match[3] = " " + match[3] + " ";
}
return match.slice( 0, 4 );
},
"CHILD": function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
match[1] = match[1].toLowerCase();
if ( match[1].slice( 0, 3 ) === "nth" ) {
// nth-* requires argument
if ( !match[3] ) {
Sizzle.error( match[0] );
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
// other types prohibit arguments
} else if ( match[3] ) {
Sizzle.error( match[0] );
}
return match;
},
"PSEUDO": function( match ) {
var excess,
unquoted = !match[6] && match[2];
if ( matchExpr["CHILD"].test( match[0] ) ) {
return null;
}
// Accept quoted arguments as-is
if ( match[3] ) {
match[2] = match[4] || match[5] || "";
// Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) &&
// Get excess from tokenize (recursively)
(excess = tokenize( unquoted, true )) &&
// advance to the next closing parenthesis
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
// excess is a negative index
match[0] = match[0].slice( 0, excess );
match[2] = unquoted.slice( 0, excess );
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
},
filter: {
"TAG": function( nodeNameSelector ) {
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
return nodeNameSelector === "*" ?
function() { return true; } :
function( elem ) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
},
"CLASS": function( className ) {
var pattern = classCache[ className + " " ];
return pattern ||
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
classCache( className, function( elem ) {
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
});
},
"ATTR": function( name, operator, check ) {
return function( elem ) {
var result = Sizzle.attr( elem, name );
if ( result == null ) {
return operator === "!=";
}
if ( !operator ) {
return true;
}
result += "";
return operator === "=" ? result === check :
operator === "!=" ? result !== check :
operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check :
operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false;
};
},
"CHILD": function( type, what, argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type";
return first === 1 && last === 0 ?
// Shortcut for :nth-*(n)
function( elem ) {
return !!elem.parentNode;
} :
function( elem, context, xml ) {
var cache, outerCache, node, diff, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType;
if ( parent ) {
// :(first|last|only)-(child|of-type)
if ( simple ) {
while ( dir ) {
node = elem;
while ( (node = node[ dir ]) ) {
if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [ forward ? parent.firstChild : parent.lastChild ];
// non-xml :nth-child(...) stores cache data on `parent`
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
outerCache = parent[ expando ] || (parent[ expando ] = {});
cache = outerCache[ type ] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = cache[0] === dirruns && cache[2];
node = nodeIndex && parent.childNodes[ nodeIndex ];
while ( (node = ++nodeIndex && node && node[ dir ] ||
// Fallback to seeking `elem` from the start
(diff = nodeIndex = 0) || start.pop()) ) {
// When found, cache indexes on `parent` and break
if ( node.nodeType === 1 && ++diff && node === elem ) {
outerCache[ type ] = [ dirruns, nodeIndex, diff ];
break;
}
}
// Use previously-cached element index if available
} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
diff = cache[1];
// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
} else {
// Use the same loop as above to seek `elem` from the start
while ( (node = ++nodeIndex && node && node[ dir ] ||
(diff = nodeIndex = 0) || start.pop()) ) {
if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
// Cache the index of each encountered element
if ( useCache ) {
(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
}
if ( node === elem ) {
break;
}
}
}
}
// Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
};
},
"PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo );
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
}
// But maintain support for old signatures
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
markFunction(function( seed, matches ) {
var idx,
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
idx = indexOf.call( seed, matched[i] );
seed[ idx ] = !( matches[ idx ] = matched[i] );
}
}) :
function( elem ) {
return fn( elem, 0, args );
};
}
return fn;
}
},
pseudos: {
// Potentially complex pseudos
"not": markFunction(function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) );
return matcher[ expando ] ?
markFunction(function( seed, matches, context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length;
// Match elements unmatched by `matcher`
while ( i-- ) {
if ( (elem = unmatched[i]) ) {
seed[i] = !(matches[i] = elem);
}
}
}) :
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
return !results.pop();
};
}),
"has": markFunction(function( selector ) {
return function( elem ) {
return Sizzle( selector, elem ).length > 0;
};
}),
"contains": markFunction(function( text ) {
return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
};
}),
// "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test(lang || "") ) {
Sizzle.error( "unsupported lang: " + lang );
}
lang = lang.replace( runescape, funescape ).toLowerCase();
return function( elem ) {
var elemLang;
do {
if ( (elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
}
} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
return false;
};
}),
// Miscellaneous
"target": function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
},
"root": function( elem ) {
return elem === docElem;
},
"focus": function( elem ) {
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
},
// Boolean properties
"enabled": function( elem ) {
return elem.disabled === false;
},
"disabled": function( elem ) {
return elem.disabled === true;
},
"checked": function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
},
"selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
// Contents
"empty": function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
// but not by others (comment: 8; processing instruction: 7; etc.)
// nodeType < 6 works because attributes (2) do not appear as children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
if ( elem.nodeType < 6 ) {
return false;
}
}
return true;
},
"parent": function( elem ) {
return !Expr.pseudos["empty"]( elem );
},
// Element/input types
"header": function( elem ) {
return rheader.test( elem.nodeName );
},
"input": function( elem ) {
return rinputs.test( elem.nodeName );
},
"button": function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
"text": function( elem ) {
var attr;
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
// Support: IE<8
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
},
// Position-in-collection
"first": createPositionalPseudo(function() {
return [ 0 ];
}),
"last": createPositionalPseudo(function( matchIndexes, length ) {
return [ length - 1 ];
}),
"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
return [ argument < 0 ? argument + length : argument ];
}),
"even": createPositionalPseudo(function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"odd": createPositionalPseudo(function( matchIndexes, length ) {
var i = 1;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; --i >= 0; ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; ++i < length; ) {
matchIndexes.push( i );
}
return matchIndexes;
})
}
};
Expr.pseudos["nth"] = Expr.pseudos["eq"];
// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
Expr.pseudos[ i ] = createButtonPseudo( i );
}
// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();
tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ];
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while ( soFar ) {
// Comma and first run
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[0].length ) || soFar;
}
groups.push( (tokens = []) );
}
matched = false;
// Combinators
if ( (match = rcombinators.exec( soFar )) ) {
matched = match.shift();
tokens.push({
value: matched,
// Cast descendant combinators to space
type: match[0].replace( rtrim, " " )
});
soFar = soFar.slice( matched.length );
}
// Filters
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
(match = preFilters[ type ]( match ))) ) {
matched = match.shift();
tokens.push({
value: matched,
type: type,
matches: match
});
soFar = soFar.slice( matched.length );
}
}
if ( !matched ) {
break;
}
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
};
function toSelector( tokens ) {
var i = 0,
len = tokens.length,
selector = "";
for ( ; i < len; i++ ) {
selector += tokens[i].value;
}
return selector;
}
function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
checkNonElements = base && dir === "parentNode",
doneName = done++;
return combinator.first ?
// Check against closest ancestor/preceding element
function( elem, context, xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
return matcher( elem, context, xml );
}
}
} :
// Check against all ancestor/preceding elements
function( elem, context, xml ) {
var oldCache, outerCache,
newCache = [ dirruns, doneName ];
// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
if ( xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
if ( matcher( elem, context, xml ) ) {
return true;
}
}
}
} else {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
outerCache = elem[ expando ] || (elem[ expando ] = {});
if ( (oldCache = outerCache[ dir ]) &&
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
// Assign to newCache so results back-propagate to previous elements
return (newCache[ 2 ] = oldCache[ 2 ]);
} else {
// Reuse newcache so results back-propagate to previous elements
outerCache[ dir ] = newCache;
// A match means we're done; a fail means we have to keep checking
if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
return true;
}
}
}
}
}
};
}
function elementMatcher( matchers ) {
return matchers.length > 1 ?
function( elem, context, xml ) {
var i = matchers.length;
while ( i-- ) {
if ( !matchers[i]( elem, context, xml ) ) {
return false;
}
}
return true;
} :
matchers[0];
}
function multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results );
}
return results;
}
function condense( unmatched, map, filter, context, xml ) {
var elem,
newUnmatched = [],
i = 0,
len = unmatched.length,
mapped = map != null;
for ( ; i < len; i++ ) {
if ( (elem = unmatched[i]) ) {
if ( !filter || filter( elem, context, xml ) ) {
newUnmatched.push( elem );
if ( mapped ) {
map.push( i );
}
}
}
}
return newUnmatched;
}
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
if ( postFilter && !postFilter[ expando ] ) {
postFilter = setMatcher( postFilter );
}
if ( postFinder && !postFinder[ expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction(function( seed, results, context, xml ) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length,
// Get initial elements from seed or context
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
// Prefilter to get matcher input, preserving a map for seed-results synchronization
matcherIn = preFilter && ( seed || !selector ) ?
condense( elems, preMap, preFilter, context, xml ) :
elems,
matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
// ...intermediate processing is necessary
[] :
// ...otherwise use results directly
results :
matcherIn;
// Find primary matches
if ( matcher ) {
matcher( matcherIn, matcherOut, context, xml );
}
// Apply postFilter
if ( postFilter ) {
temp = condense( matcherOut, postMap );
postFilter( temp, [], context, xml );
// Un-match failing elements by moving them back to matcherIn
i = temp.length;
while ( i-- ) {
if ( (elem = temp[i]) ) {
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
}
}
}
if ( seed ) {
if ( postFinder || preFilter ) {
if ( postFinder ) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) ) {
// Restore matcherIn since elem is not yet a final match
temp.push( (matcherIn[i] = elem) );
}
}
postFinder( null, (matcherOut = []), temp, xml );
}
// Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
seed[temp] = !(results[temp] = elem);
}
}
}
// Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice( preexisting, matcherOut.length ) :
matcherOut
);
if ( postFinder ) {
postFinder( null, results, matcherOut, xml );
} else {
push.apply( results, matcherOut );
}
}
});
}
function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[ tokens[0].type ],
implicitRelative = leadingRelative || Expr.relative[" "],
i = leadingRelative ? 1 : 0,
// The foundational matcher ensures that elements are reachable from top-level context(s)
matchContext = addCombinator( function( elem ) {
return elem === checkContext;
}, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) {
return indexOf.call( checkContext, elem ) > -1;
}, implicitRelative, true ),
matchers = [ function( elem, context, xml ) {
return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
(checkContext = context).nodeType ?
matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) );
} ];
for ( ; i < len; i++ ) {
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
} else {
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
// Return special upon seeing a positional matcher
if ( matcher[ expando ] ) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for ( ; j < len; j++ ) {
if ( Expr.relative[ tokens[j].type ] ) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher( matchers ),
i > 1 && toSelector(
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
).replace( rtrim, "$1" ),
matcher,
i < j && matcherFromTokens( tokens.slice( i, j ) ),
j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
j < len && toSelector( tokens )
);
}
matchers.push( matcher );
}
}
return elementMatcher( matchers );
}
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
var bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
superMatcher = function( seed, context, xml, results, outermost ) {
var elem, j, matcher,
matchedCount = 0,
i = "0",
unmatched = seed && [],
setMatched = [],
contextBackup = outermostContext,
// We must always have either seed elements or outermost context
elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
len = elems.length;
if ( outermost ) {
outermostContext = context !== document && context;
}
// Add elements passing elementMatchers directly to results
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
// Support: IE<9, Safari
// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
if ( byElement && elem ) {
j = 0;
while ( (matcher = elementMatchers[j++]) ) {
if ( matcher( elem, context, xml ) ) {
results.push( elem );
break;
}
}
if ( outermost ) {
dirruns = dirrunsUnique;
}
}
// Track unmatched elements for set filters
if ( bySet ) {
// They will have gone through all possible matchers
if ( (elem = !matcher && elem) ) {
matchedCount--;
}
// Lengthen the array for every element, matched or not
if ( seed ) {
unmatched.push( elem );
}
}
}
// Apply set filters to unmatched elements
matchedCount += i;
if ( bySet && i !== matchedCount ) {
j = 0;
while ( (matcher = setMatchers[j++]) ) {
matcher( unmatched, setMatched, context, xml );
}
if ( seed ) {
// Reintegrate element matches to eliminate the need for sorting
if ( matchedCount > 0 ) {
while ( i-- ) {
if ( !(unmatched[i] || setMatched[i]) ) {
setMatched[i] = pop.call( results );
}
}
}
// Discard index placeholder values to get only actual matches
setMatched = condense( setMatched );
}
// Add matches to results
push.apply( results, setMatched );
// Seedless set matches succeeding multiple successful matchers stipulate sorting
if ( outermost && !seed && setMatched.length > 0 &&
( matchedCount + setMatchers.length ) > 1 ) {
Sizzle.uniqueSort( results );
}
}
// Override manipulation of globals by nested matchers
if ( outermost ) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
}
return unmatched;
};
return bySet ?
markFunction( superMatcher ) :
superMatcher;
}
compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[ selector + " " ];
if ( !cached ) {
// Generate a function of recursive functions that can be used to check each element
if ( !match ) {
match = tokenize( selector );
}
i = match.length;
while ( i-- ) {
cached = matcherFromTokens( match[i] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
}
// Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
// Save selector and tokenization
cached.selector = selector;
}
return cached;
};
/**
* A low-level selection function that works with Sizzle's compiled
* selector functions
* @param {String|Function} selector A selector or a pre-compiled
* selector function built with Sizzle.compile
* @param {Element} context
* @param {Array} [results]
* @param {Array} [seed] A set of elements to match against
*/
select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find,
compiled = typeof selector === "function" && selector,
match = !seed && tokenize( (selector = compiled.selector || selector) );
results = results || [];
// Try to minimize operations if there is no seed and only one group
if ( match.length === 1 ) {
// Take a shortcut and set the context if the root selector is an ID
tokens = match[0] = match[0].slice( 0 );
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
support.getById && context.nodeType === 9 && documentIsHTML &&
Expr.relative[ tokens[1].type ] ) {
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
if ( !context ) {
return results;
// Precompiled matchers will still verify ancestry, so step up a level
} else if ( compiled ) {
context = context.parentNode;
}
selector = selector.slice( tokens.shift().value.length );
}
// Fetch a seed set for right-to-left matching
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
while ( i-- ) {
token = tokens[i];
// Abort if we hit a combinator
if ( Expr.relative[ (type = token.type) ] ) {
break;
}
if ( (find = Expr.find[ type ]) ) {
// Search, expanding context for leading sibling combinators
if ( (seed = find(
token.matches[0].replace( runescape, funescape ),
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
)) ) {
// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
}
break;
}
}
}
}
// Compile and execute a filtering function if one is not provided
// Provide `match` to avoid retokenization if we modified the selector above
( compiled || compile( selector, match ) )(
seed,
context,
!documentIsHTML,
results,
rsibling.test( selector ) && testContext( context.parentNode ) || context
);
return results;
};
// One-time assignments
// Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
// Support: Chrome<14
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;
// Initialize against the default document
setDocument();
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function( div1 ) {
// Should return 1, but returns 4 (following)
return div1.compareDocumentPosition( document.createElement("div") ) & 1;
});
// Support: IE<8
// Prevent attribute/property "interpolation"
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert(function( div ) {
div.innerHTML = "<a href='#'></a>";
return div.firstChild.getAttribute("href") === "#" ;
}) ) {
addHandle( "type|href|height|width", function( elem, name, isXML ) {
if ( !isXML ) {
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
}
});
}
// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert(function( div ) {
div.innerHTML = "<input/>";
div.firstChild.setAttribute( "value", "" );
return div.firstChild.getAttribute( "value" ) === "";
}) ) {
addHandle( "value", function( elem, name, isXML ) {
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
return elem.defaultValue;
}
});
}
// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert(function( div ) {
return div.getAttribute("disabled") == null;
}) ) {
addHandle( booleans, function( elem, name, isXML ) {
var val;
if ( !isXML ) {
return elem[ name ] === true ? name.toLowerCase() :
(val = elem.getAttributeNode( name )) && val.specified ?
val.value :
null;
}
});
}
return Sizzle;
})( window );
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
var rneedsContext = jQuery.expr.match.needsContext;
var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
var risSimple = /^.[^:#\[\.,]*$/;
// Implement the identical functionality for filter and not
function winnow( elements, qualifier, not ) {
if ( jQuery.isFunction( qualifier ) ) {
return jQuery.grep( elements, function( elem, i ) {
/* jshint -W018 */
return !!qualifier.call( elem, i, elem ) !== not;
});
}
if ( qualifier.nodeType ) {
return jQuery.grep( elements, function( elem ) {
return ( elem === qualifier ) !== not;
});
}
if ( typeof qualifier === "string" ) {
if ( risSimple.test( qualifier ) ) {
return jQuery.filter( qualifier, elements, not );
}
qualifier = jQuery.filter( qualifier, elements );
}
return jQuery.grep( elements, function( elem ) {
return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
});
}
jQuery.filter = function( expr, elems, not ) {
var elem = elems[ 0 ];
if ( not ) {
expr = ":not(" + expr + ")";
}
return elems.length === 1 && elem.nodeType === 1 ?
jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
return elem.nodeType === 1;
}));
};
jQuery.fn.extend({
find: function( selector ) {
var i,
ret = [],
self = this,
len = self.length;
if ( typeof selector !== "string" ) {
return this.pushStack( jQuery( selector ).filter(function() {
for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {
return true;
}
}
}) );
}
for ( i = 0; i < len; i++ ) {
jQuery.find( selector, self[ i ], ret );
}
// Needed because $( selector, context ) becomes $( context ).find( selector )
ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
ret.selector = this.selector ? this.selector + " " + selector : selector;
return ret;
},
filter: function( selector ) {
return this.pushStack( winnow(this, selector || [], false) );
},
not: function( selector ) {
return this.pushStack( winnow(this, selector || [], true) );
},
is: function( selector ) {
return !!winnow(
this,
// If this is a positional/relative selector, check membership in the returned set
// so $("p:first").is("p:last") won't return true for a doc with two "p".
typeof selector === "string" && rneedsContext.test( selector ) ?
jQuery( selector ) :
selector || [],
false
).length;
}
});
// Initialize a jQuery object
// A central reference to the root jQuery(document)
var rootjQuery,
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
// A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
// scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
return this;
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}
// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return typeof rootjQuery.ready !== "undefined" ?
rootjQuery.ready( selector ) :
// Execute immediately if ready is not present
selector( jQuery );
}
if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
};
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
// Initialize central reference
rootjQuery = jQuery( document );
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
// methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
jQuery.extend({
dir: function( elem, dir, until ) {
var matched = [],
cur = elem[ dir ];
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
cur = cur[dir];
}
return matched;
},
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
});
jQuery.fn.extend({
has: function( target ) {
var i,
targets = jQuery( target, this ),
len = targets.length;
return this.filter(function() {
for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {
return true;
}
}
});
},
closest: function( selectors, context ) {
var cur,
i = 0,
l = this.length,
matched = [],
pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;
for ( ; i < l; i++ ) {
for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
// Always skip document fragments
if ( cur.nodeType < 11 && (pos ?
pos.index(cur) > -1 :
// Don't pass non-elements to Sizzle
cur.nodeType === 1 &&
jQuery.find.matchesSelector(cur, selectors)) ) {
matched.push( cur );
break;
}
}
}
return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
},
// Determine the position of an element within
// the matched set of elements
index: function( elem ) {
// No argument, return index in parent
if ( !elem ) {
return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
}
// index in selector
if ( typeof elem === "string" ) {
return jQuery.inArray( this[0], jQuery( elem ) );
}
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[0] : elem, this );
},
add: function( selector, context ) {
return this.pushStack(
jQuery.unique(
jQuery.merge( this.get(), jQuery( selector, context ) )
)
);
},
addBack: function( selector ) {
return this.add( selector == null ?
this.prevObject : this.prevObject.filter(selector)
);
}
});
function sibling( cur, dir ) {
do {
cur = cur[ dir ];
} while ( cur && cur.nodeType !== 1 );
return cur;
}
jQuery.each({
parent: function( elem ) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return jQuery.dir( elem, "parentNode" );
},
parentsUntil: function( elem, i, until ) {
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return jQuery.dir( elem, "previousSibling" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
},
contents: function( elem ) {
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );
if ( name.slice( -5 ) !== "Until" ) {
selector = until;
}
if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}
if ( this.length > 1 ) {
// Remove duplicates
if ( !guaranteedUnique[ name ] ) {
ret = jQuery.unique( ret );
}
// Reverse order for parents* and prev-derivatives
if ( rparentsprev.test( name ) ) {
ret = ret.reverse();
}
}
return this.pushStack( ret );
};
});
var rnotwhite = (/\S+/g);
// String to Object options format cache
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
}
/*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
var // Flag to know if list is currently firing
firing,
// Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// First callback to fire (used internally by add and fireWith)
firingStart,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
stack = !options.once && [],
// Fire callbacks
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
empty: function() {
list = [];
firingLength = 0;
return this;
},
// Have the list do nothing anymore
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function() {
return !list;
},
// Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// Is it locked?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};
return self;
};
jQuery.extend({
Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
state = "pending",
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ](function() {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat
promise.pipe = promise.then;
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[1] ] = list.add;
// Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
// deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
deferred[ tuple[0] + "With" ] = list.fireWith;
});
// Make the deferred a promise
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
// Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
if ( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !(--remaining) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) )
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
});
// The deferred used on DOM ready
var readyList;
jQuery.fn.ready = function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
};
jQuery.extend({
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
// Abort if there are pending holds or we're already ready
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready );
}
// Remember that the DOM is ready
jQuery.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
// Trigger any bound ready events
if ( jQuery.fn.triggerHandler ) {
jQuery( document ).triggerHandler( "ready" );
jQuery( document ).off( "ready" );
}
}
});
/**
* Clean-up method for dom ready events
*/
function detach() {
if ( document.addEventListener ) {
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
} else {
document.detachEvent( "onreadystatechange", completed );
window.detachEvent( "onload", completed );
}
}
/**
* The ready event handler and self cleanup method
*/
function completed() {
// readyState === "complete" is good enough for us to call the dom ready in oldIE
if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
detach();
jQuery.ready();
}
}
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {
readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
// If IE event model is used
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", completed );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", completed );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// detach all dom ready events
detach();
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
return readyList.promise( obj );
};
var strundefined = typeof undefined;
// Support: IE<9
// Iteration over object's inherited properties before its own
var i;
for ( i in jQuery( support ) ) {
break;
}
support.ownLast = i !== "0";
// Note: most support tests are defined in their respective modules.
// false until the test is run
support.inlineBlockNeedsLayout = false;
// Execute ASAP in case we need to set body.style.zoom
jQuery(function() {
// Minified: var a,b,c,d
var val, div, body, container;
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body || !body.style ) {
// Return for frameset docs that don't have a body
return;
}
// Setup
div = document.createElement( "div" );
container = document.createElement( "div" );
container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
body.appendChild( container ).appendChild( div );
if ( typeof div.style.zoom !== strundefined ) {
// Support: IE<8
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
if ( val ) {
// Prevent IE 6 from affecting layout for positioned elements #11048
// Prevent IE from shrinking the body in IE 7 mode #12869
// Support: IE<8
body.style.zoom = 1;
}
}
body.removeChild( container );
});
(function() {
var div = document.createElement( "div" );
// Execute the test only if not already executed in another module.
if (support.deleteExpando == null) {
// Support: IE<9
support.deleteExpando = true;
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
}
// Null elements to avoid leaks in IE.
div = null;
})();
/**
* Determines whether an object can have data
*/
jQuery.acceptData = function( elem ) {
var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
nodeType = +elem.nodeType || 1;
// Do not set data on non-element DOM nodes because it will not be cleared (#8335).
return nodeType !== 1 && nodeType !== 9 ?
false :
// Nodes accept data unless otherwise specified; rejection can be conditional
!noData || noData !== true && elem.getAttribute("classid") === noData;
};
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
rmultiDash = /([A-Z])/g;
function dataAttr( elem, key, data ) {
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
var name;
for ( name in obj ) {
// if the public data object is empty, the private is still empty
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
continue;
}
if ( name !== "toJSON" ) {
return false;
}
}
return true;
}
function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var ret, thisCache,
internalKey = jQuery.expando,
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
} else {
id = internalKey;
}
}
if ( !cache[ id ] ) {
// Avoid exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
// data.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( typeof name === "string" ) {
// First Try to find as-is property data
ret = thisCache[ name ];
// Test for null|undefined property data
if ( ret == null ) {
// Try to find the camelCased property
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
ret = thisCache;
}
return ret;
}
function internalRemoveData( elem, name, pvt ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var thisCache, i,
isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
if ( name ) {
thisCache = pvt ? cache[ id ] : cache[ id ].data;
if ( thisCache ) {
// Support array or space separated string names for data keys
if ( !jQuery.isArray( name ) ) {
// try the string as a key before any manipulation
if ( name in thisCache ) {
name = [ name ];
} else {
// split the camel cased version by spaces unless a key with the spaces exists
name = jQuery.camelCase( name );
if ( name in thisCache ) {
name = [ name ];
} else {
name = name.split(" ");
}
}
} else {
// If "name" is an array of keys...
// When data is initially created, via ("key", "val") signature,
// keys will be converted to camelCase.
// Since there is no way to tell _how_ a key was added, remove
// both plain key and camelCase key. #12786
// This will only penalize the array argument path.
name = name.concat( jQuery.map( name, jQuery.camelCase ) );
}
i = name.length;
while ( i-- ) {
delete thisCache[ name[i] ];
}
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
return;
}
}
}
// See jQuery.data for more information
if ( !pvt ) {
delete cache[ id ].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
// Destroy the cache
if ( isNode ) {
jQuery.cleanData( [ elem ], true );
// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
/* jshint eqeqeq: false */
} else if ( support.deleteExpando || cache != cache.window ) {
/* jshint eqeqeq: true */
delete cache[ id ];
// When all else fails, null
} else {
cache[ id ] = null;
}
}
jQuery.extend({
cache: {},
// The following elements (space-suffixed to avoid Object.prototype collisions)
// throw uncatchable exceptions if you attempt to set expando properties
noData: {
"applet ": true,
"embed ": true,
// ...but Flash objects (which have this classid) *can* handle expandos
"object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem );
},
data: function( elem, name, data ) {
return internalData( elem, name, data );
},
removeData: function( elem, name ) {
return internalRemoveData( elem, name );
},
// For internal use only.
_data: function( elem, name, data ) {
return internalData( elem, name, data, true );
},
_removeData: function( elem, name ) {
return internalRemoveData( elem, name, true );
}
});
jQuery.fn.extend({
data: function( key, value ) {
var i, name, data,
elem = this[0],
attrs = elem && elem.attributes;
// Special expections of .data basically thwart jQuery.access,
// so implement the relevant behavior ourselves
// Gets all values
if ( key === undefined ) {
if ( this.length ) {
data = jQuery.data( elem );
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
i = attrs.length;
while ( i-- ) {
// Support: IE11+
// The attrs elements can be null (#14894)
if ( attrs[ i ] ) {
name = attrs[ i ].name;
if ( name.indexOf( "data-" ) === 0 ) {
name = jQuery.camelCase( name.slice(5) );
dataAttr( elem, name, data[ name ] );
}
}
}
jQuery._data( elem, "parsedAttrs", true );
}
}
return data;
}
// Sets multiple values
if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
return arguments.length > 1 ?
// Sets one value
this.each(function() {
jQuery.data( this, key, value );
}) :
// Gets one value
// Try to fetch any internally stored data first
elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
},
removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
jQuery.extend({
queue: function( elem, type, data ) {
var queue;
if ( elem ) {
type = ( type || "fx" ) + "queue";
queue = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !queue || jQuery.isArray(data) ) {
queue = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
queue.push( data );
}
}
return queue || [];
}
},
dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ),
startLength = queue.length,
fn = queue.shift(),
hooks = jQuery._queueHooks( elem, type ),
next = function() {
jQuery.dequeue( elem, type );
};
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
startLength--;
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
// clear up the last queue stop function
delete hooks.stop;
fn.call( elem, next, hooks );
}
if ( !startLength && hooks ) {
hooks.empty.fire();
}
},
// not intended for public consumption - generates a queueHooks object, or returns the current one
_queueHooks: function( elem, type ) {
var key = type + "queueHooks";
return jQuery._data( elem, key ) || jQuery._data( elem, key, {
empty: jQuery.Callbacks("once memory").add(function() {
jQuery._removeData( elem, type + "queue" );
jQuery._removeData( elem, key );
})
});
}
});
jQuery.fn.extend({
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter--;
}
if ( arguments.length < setter ) {
return jQuery.queue( this[0], type );
}
return data === undefined ?
this :
this.each(function() {
var queue = jQuery.queue( this, type, data );
// ensure a hooks for this queue
jQuery._queueHooks( this, type );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
clearQueue: function( type ) {
return this.queue( type || "fx", [] );
},
// Get a promise resolved when queues of a certain type
// are emptied (fx is the type by default)
promise: function( type, obj ) {
var tmp,
count = 1,
defer = jQuery.Deferred(),
elements = this,
i = this.length,
resolve = function() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
};
if ( typeof type !== "string" ) {
obj = type;
type = undefined;
}
type = type || "fx";
while ( i-- ) {
tmp = jQuery._data( elements[ i ], type + "queueHooks" );
if ( tmp && tmp.empty ) {
count++;
tmp.empty.add( resolve );
}
}
resolve();
return defer.promise( obj );
}
});
var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
var isHidden = function( elem, el ) {
// isHidden might be called from jQuery#filter function;
// in that case, element will be second argument
elem = el || elem;
return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
};
// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
length = elems.length,
bulk = key == null;
// Sets many values
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
}
// Sets one value
} else if ( value !== undefined ) {
chainable = true;
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
if ( bulk ) {
// Bulk operations run against the entire set
if ( raw ) {
fn.call( elems, value );
fn = null;
// ...except when executing function values
} else {
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
if ( fn ) {
for ( ; i < length; i++ ) {
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
}
return chainable ?
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};
var rcheckableType = (/^(?:checkbox|radio)$/i);
(function() {
// Minified: var a,b,c
var input = document.createElement( "input" ),
div = document.createElement( "div" ),
fragment = document.createDocumentFragment();
// Setup
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
// IE strips leading whitespace when .innerHTML is used
support.leadingWhitespace = div.firstChild.nodeType === 3;
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
support.tbody = !div.getElementsByTagName( "tbody" ).length;
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
// Makes sure cloning an html5 element does not cause problems
// Where outerHTML is undefined, this still works
support.html5Clone =
document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
input.type = "checkbox";
input.checked = true;
fragment.appendChild( input );
support.appendChecked = input.checked;
// Make sure textarea (and checkbox) defaultValue is properly cloned
// Support: IE6-IE11+
div.innerHTML = "<textarea>x</textarea>";
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
// #11217 - WebKit loses check when the name is after the checked attribute
fragment.appendChild( div );
div.innerHTML = "<input type='radio' checked='checked' name='t'/>";
// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
// old WebKit doesn't clone checked state correctly in fragments
support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
// Support: IE<9
// Opera does not clone events (and typeof div.attachEvent === undefined).
// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
support.noCloneEvent = true;
if ( div.attachEvent ) {
div.attachEvent( "onclick", function() {
support.noCloneEvent = false;
});
div.cloneNode( true ).click();
}
// Execute the test only if not already executed in another module.
if (support.deleteExpando == null) {
// Support: IE<9
support.deleteExpando = true;
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
}
})();
(function() {
var i, eventName,
div = document.createElement( "div" );
// Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
for ( i in { submit: true, change: true, focusin: true }) {
eventName = "on" + i;
if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
div.setAttribute( eventName, "t" );
support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
}
}
// Null elements to avoid leaks in IE.
div = null;
})();
var rformElems = /^(?:input|select|textarea)$/i,
rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
function safeActiveElement() {
try {
return document.activeElement;
} catch ( err ) { }
}
/*
* Helper functions for managing events -- not part of the public interface.
* Props to Dean Edwards' addEvent library for many of the ideas.
*/
jQuery.event = {
global: {},
add: function( elem, types, handler, data, selector ) {
var tmp, events, t, handleObjIn,
special, eventHandle, handleObj,
handlers, type, namespaces, origType,
elemData = jQuery._data( elem );
// Don't attach events to noData or text/comment nodes (but allow plain objects)
if ( !elemData ) {
return;
}
// Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
// Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// Init the element's event structure and main handler, if this is the first
if ( !(events = elemData.events) ) {
events = elemData.events = {};
}
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
// Handle multiple events separated by a space
types = ( types || "" ).match( rnotwhite ) || [ "" ];
t = types.length;
while ( t-- ) {
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
namespaces = ( tmp[2] || "" ).split( "." ).sort();
// There *must* be a type, no attaching namespace-only handlers
if ( !type ) {
continue;
}
// If event changes its type, use the special event handlers for the changed type
special = jQuery.event.special[ type ] || {};
// If selector defined, determine special event api type, otherwise given type
type = ( selector ? special.delegateType : special.bindType ) || type;
// Update special based on newly reset type
special = jQuery.event.special[ type ] || {};
// handleObj is passed to all event handlers
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn );
// Init the event handler queue if we're the first
if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// Only use addEventListener/attachEvent if the special events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// Add to the element's handler list, delegates in front
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
}
// Nullify elem to prevent memory leaks in IE
elem = null;
},
// Detach an event or set of events from an element
remove: function( elem, types, handler, selector, mappedTypes ) {
var j, handleObj, tmp,
origCount, t, events,
special, handlers, type,
namespaces, origType,
elemData = jQuery.hasData( elem ) && jQuery._data( elem );
if ( !elemData || !(events = elemData.events) ) {
return;
}
// Once for each type.namespace in types; type may be omitted
types = ( types || "" ).match( rnotwhite ) || [ "" ];
t = types.length;
while ( t-- ) {
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
namespaces = ( tmp[2] || "" ).split( "." ).sort();
// Unbind all events (on this namespace, if provided) for the element
if ( !type ) {
for ( type in events ) {
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
}
continue;
}
special = jQuery.event.special[ type ] || {};
type = ( selector ? special.delegateType : special.bindType ) || type;
handlers = events[ type ] || [];
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
// Remove matching events
origCount = j = handlers.length;
while ( j-- ) {
handleObj = handlers[ j ];
if ( ( mappedTypes || origType === handleObj.origType ) &&
( !handler || handler.guid === handleObj.guid ) &&
( !tmp || tmp.test( handleObj.namespace ) ) &&
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
handlers.splice( j, 1 );
if ( handleObj.selector ) {
handlers.delegateCount--;
}
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
}
// Remove generic event handler if we removed something and no more handlers exist
// (avoids potential for endless recursion during removal of special event handlers)
if ( origCount && !handlers.length ) {
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
delete events[ type ];
}
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
delete elemData.handle;
// removeData also checks for emptiness and clears the expando if empty
// so use it instead of delete
jQuery._removeData( elem, "events" );
}
},
trigger: function( event, data, elem, onlyHandlers ) {
var handle, ontype, cur,
bubbleType, special, tmp, i,
eventPath = [ elem || document ],
type = hasOwn.call( event, "type" ) ? event.type : event,
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
cur = tmp = elem = elem || document;
// Don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
// focus/blur morphs to focusin/out; ensure we're not firing them right now
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
}
if ( type.indexOf(".") >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf(":") < 0 && "on" + type;
// Caller can pass in a jQuery.Event object, Object, or just an event type string
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event );
// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join(".");
event.namespace_re = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
null;
// Clean up the event in case it is being reused
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}
// Clone any incoming data and prepend the event, creating the handler arg list
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] );
// Allow special events to draw outside the lines
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
bubbleType = special.delegateType || type;
if ( !rfocusMorph.test( bubbleType + type ) ) {
cur = cur.parentNode;
}
for ( ; cur; cur = cur.parentNode ) {
eventPath.push( cur );
tmp = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( tmp === (elem.ownerDocument || document) ) {
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
}
}
// Fire handlers on the event path
i = 0;
while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
event.type = i > 1 ?
bubbleType :
special.bindType || type;
// jQuery handler
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
// Native handler
handle = ontype && cur[ ontype ];
if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
event.result = handle.apply( cur, data );
if ( event.result === false ) {
event.preventDefault();
}
}
}
event.type = type;
// If nobody prevented the default action, do it now
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
jQuery.acceptData( elem ) ) {
// Call a native DOM method on the target with the same name name as the event.
// Can't use an .isFunction() check here because IE6/7 fails that test.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method
tmp = elem[ ontype ];
if ( tmp ) {
elem[ ontype ] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
try {
elem[ type ]();
} catch ( e ) {
// IE<9 dies on focus/blur to hidden element (#1486,#12518)
// only reproducible on winXP IE8 native, not IE9 in IE8 mode
}
jQuery.event.triggered = undefined;
if ( tmp ) {
elem[ ontype ] = tmp;
}
}
}
}
return event.result;
},
dispatch: function( event ) {
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event );
var i, ret, handleObj, matched, j,
handlerQueue = [],
args = slice.call( arguments ),
handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
special = jQuery.event.special[ event.type ] || {};
// Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
event.delegateTarget = this;
// Call the preDispatch hook for the mapped type, and let it bail if desired
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
return;
}
// Determine handlers
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
// Run delegates first; they may want to stop propagation beneath us
i = 0;
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
event.currentTarget = matched.elem;
j = 0;
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
// Triggered event must either 1) have no namespace, or
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
event.handleObj = handleObj;
event.data = handleObj.data;
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
.apply( matched.elem, args );
if ( ret !== undefined ) {
if ( (event.result = ret) === false ) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
// Call the postDispatch hook for the mapped type
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
return event.result;
},
handlers: function( event, handlers ) {
var sel, handleObj, matches, i,
handlerQueue = [],
delegateCount = handlers.delegateCount,
cur = event.target;
// Find delegate handlers
// Black-hole SVG <use> instance trees (#13180)
// Avoid non-left-click bubbling in Firefox (#3861)
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
/* jshint eqeqeq: false */
for ( ; cur != this; cur = cur.parentNode || this ) {
/* jshint eqeqeq: true */
// Don't check non-elements (#13208)
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
// Don't conflict with Object.prototype properties (#13203)
sel = handleObj.selector + " ";
if ( matches[ sel ] === undefined ) {
matches[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :
jQuery.find( sel, this, null, [ cur ] ).length;
}
if ( matches[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
}
}
// Add the remaining (directly-bound) handlers
if ( delegateCount < handlers.length ) {
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
}
return handlerQueue;
},
fix: function( event ) {
if ( event[ jQuery.expando ] ) {
return event;
}
// Create a writable copy of the event object and normalize some properties
var i, prop, copy,
type = event.type,
originalEvent = event,
fixHook = this.fixHooks[ type ];
if ( !fixHook ) {
this.fixHooks[ type ] = fixHook =
rmouseEvent.test( type ) ? this.mouseHooks :
rkeyEvent.test( type ) ? this.keyHooks :
{};
}
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
event = new jQuery.Event( originalEvent );
i = copy.length;
while ( i-- ) {
prop = copy[ i ];
event[ prop ] = originalEvent[ prop ];
}
// Support: IE<9
// Fix target property (#1925)
if ( !event.target ) {
event.target = originalEvent.srcElement || document;
}
// Support: Chrome 23+, Safari?
// Target should not be a text node (#504, #13143)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// Support: IE<9
// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
event.metaKey = !!event.metaKey;
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
},
// Includes some event props shared by KeyEvent and MouseEvent
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
fixHooks: {},
keyHooks: {
props: "char charCode key keyCode".split(" "),
filter: function( event, original ) {
// Add which for key events
if ( event.which == null ) {
event.which = original.charCode != null ? original.charCode : original.keyCode;
}
return event;
}
},
mouseHooks: {
props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
filter: function( event, original ) {
var body, eventDoc, doc,
button = original.button,
fromElement = original.fromElement;
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && original.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
}
// Add relatedTarget, if necessary
if ( !event.relatedTarget && fromElement ) {
event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
}
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && button !== undefined ) {
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
}
return event;
}
},
special: {
load: {
// Prevent triggered image.load events from bubbling to window.load
noBubble: true
},
focus: {
// Fire native event if possible so blur/focus sequence is correct
trigger: function() {
if ( this !== safeActiveElement() && this.focus ) {
try {
this.focus();
return false;
} catch ( e ) {
// Support: IE<9
// If we error on focus to hidden element (#1486, #12518),
// let .trigger() run the handlers
}
}
},
delegateType: "focusin"
},
blur: {
trigger: function() {
if ( this === safeActiveElement() && this.blur ) {
this.blur();
return false;
}
},
delegateType: "focusout"
},
click: {
// For checkbox, fire native event so checked state will be right
trigger: function() {
if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
this.click();
return false;
}
},
// For cross-browser consistency, don't fire native .click() on links
_default: function( event ) {
return jQuery.nodeName( event.target, "a" );
}
},
beforeunload: {
postDispatch: function( event ) {
// Support: Firefox 20+
// Firefox doesn't alert if the returnValue field is not set.
if ( event.result !== undefined && event.originalEvent ) {
event.originalEvent.returnValue = event.result;
}
}
}
},
simulate: function( type, elem, event, bubble ) {
// Piggyback on a donor event to simulate a different one.
// Fake originalEvent to avoid donor's stopPropagation, but if the
// simulated event prevents default then we do the same on the donor.
var e = jQuery.extend(
new jQuery.Event(),
event,
{
type: type,
isSimulated: true,
originalEvent: {}
}
);
if ( bubble ) {
jQuery.event.trigger( e, null, elem );
} else {
jQuery.event.dispatch.call( elem, e );
}
if ( e.isDefaultPrevented() ) {
event.preventDefault();
}
}
};
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
var name = "on" + type;
if ( elem.detachEvent ) {
// #8545, #7054, preventing memory leaks for custom events in IE6-8
// detachEvent needed property on element, by name of that event, to properly expose it to GC
if ( typeof elem[ name ] === strundefined ) {
elem[ name ] = null;
}
elem.detachEvent( name, handle );
}
};
jQuery.Event = function( src, props ) {
// Allow instantiation without the 'new' keyword
if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
// Event object
if ( src && src.type ) {
this.originalEvent = src;
this.type = src.type;
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = src.defaultPrevented ||
src.defaultPrevented === undefined &&
// Support: IE < 9, Android < 4.0
src.returnValue === false ?
returnTrue :
returnFalse;
// Event type
} else {
this.type = src;
}
// Put explicitly provided properties onto the event object
if ( props ) {
jQuery.extend( this, props );
}
// Create a timestamp if incoming event doesn't have one
this.timeStamp = src && src.timeStamp || jQuery.now();
// Mark it as fixed
this[ jQuery.expando ] = true;
};
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse,
preventDefault: function() {
var e = this.originalEvent;
this.isDefaultPrevented = returnTrue;
if ( !e ) {
return;
}
// If preventDefault exists, run it on the original event
if ( e.preventDefault ) {
e.preventDefault();
// Support: IE
// Otherwise set the returnValue property of the original event to false
} else {
e.returnValue = false;
}
},
stopPropagation: function() {
var e = this.originalEvent;
this.isPropagationStopped = returnTrue;
if ( !e ) {
return;
}
// If stopPropagation exists, run it on the original event
if ( e.stopPropagation ) {
e.stopPropagation();
}
// Support: IE
// Set the cancelBubble property of the original event to true
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
var e = this.originalEvent;
this.isImmediatePropagationStopped = returnTrue;
if ( e && e.stopImmediatePropagation ) {
e.stopImmediatePropagation();
}
this.stopPropagation();
}
};
// Create mouseenter/leave events using mouseover/out and event-time checks
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout",
pointerenter: "pointerover",
pointerleave: "pointerout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
delegateType: fix,
bindType: fix,
handle: function( event ) {
var ret,
target = this,
related = event.relatedTarget,
handleObj = event.handleObj;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = fix;
}
return ret;
}
};
});
// IE submit delegation
if ( !support.submitBubbles ) {
jQuery.event.special.submit = {
setup: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Lazy-add a submit handler when a descendant form may potentially be submitted
jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
// Node name check avoids a VML-related crash in IE (#9807)
var elem = e.target,
form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
if ( form && !jQuery._data( form, "submitBubbles" ) ) {
jQuery.event.add( form, "submit._submit", function( event ) {
event._submit_bubble = true;
});
jQuery._data( form, "submitBubbles", true );
}
});
// return undefined since we don't need an event listener
},
postDispatch: function( event ) {
// If form was submitted by the user, bubble the event up the tree
if ( event._submit_bubble ) {
delete event._submit_bubble;
if ( this.parentNode && !event.isTrigger ) {
jQuery.event.simulate( "submit", this.parentNode, event, true );
}
}
},
teardown: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
jQuery.event.remove( this, "._submit" );
}
};
}
// IE change delegation and checkbox/radio fix
if ( !support.changeBubbles ) {
jQuery.event.special.change = {
setup: function() {
if ( rformElems.test( this.nodeName ) ) {
// IE doesn't fire change on a check/radio until blur; trigger it on click
// after a propertychange. Eat the blur-change in special.change.handle.
// This still fires onchange a second time for check/radio after blur.
if ( this.type === "checkbox" || this.type === "radio" ) {
jQuery.event.add( this, "propertychange._change", function( event ) {
if ( event.originalEvent.propertyName === "checked" ) {
this._just_changed = true;
}
});
jQuery.event.add( this, "click._change", function( event ) {
if ( this._just_changed && !event.isTrigger ) {
this._just_changed = false;
}
// Allow triggered, simulated change events (#11500)
jQuery.event.simulate( "change", this, event, true );
});
}
return false;
}
// Delegated event; lazy-add a change handler on descendant inputs
jQuery.event.add( this, "beforeactivate._change", function( e ) {
var elem = e.target;
if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
jQuery.event.add( elem, "change._change", function( event ) {
if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
jQuery.event.simulate( "change", this.parentNode, event, true );
}
});
jQuery._data( elem, "changeBubbles", true );
}
});
},
handle: function( event ) {
var elem = event.target;
// Swallow native change events from checkbox/radio, we already triggered them above
if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
return event.handleObj.handler.apply( this, arguments );
}
},
teardown: function() {
jQuery.event.remove( this, "._change" );
return !rformElems.test( this.nodeName );
}
};
}
// Create "bubbling" focus and blur events
if ( !support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
// Attach a single capturing handler on the document while someone wants focusin/focusout
var handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
};
jQuery.event.special[ fix ] = {
setup: function() {
var doc = this.ownerDocument || this,
attaches = jQuery._data( doc, fix );
if ( !attaches ) {
doc.addEventListener( orig, handler, true );
}
jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
},
teardown: function() {
var doc = this.ownerDocument || this,
attaches = jQuery._data( doc, fix ) - 1;
if ( !attaches ) {
doc.removeEventListener( orig, handler, true );
jQuery._removeData( doc, fix );
} else {
jQuery._data( doc, fix, attaches );
}
}
};
});
}
jQuery.fn.extend({
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var type, origFn;
// Types can be a map of types/handlers
if ( typeof types === "object" ) {
// ( types-Object, selector, data )
if ( typeof selector !== "string" ) {
// ( types-Object, data )
data = data || selector;
selector = undefined;
}
for ( type in types ) {
this.on( type, selector, data, types[ type ], one );
}
return this;
}
if ( data == null && fn == null ) {
// ( types, fn )
fn = selector;
data = selector = undefined;
} else if ( fn == null ) {
if ( typeof selector === "string" ) {
// ( types, selector, fn )
fn = data;
data = undefined;
} else {
// ( types, data, fn )
fn = data;
data = selector;
selector = undefined;
}
}
if ( fn === false ) {
fn = returnFalse;
} else if ( !fn ) {
return this;
}
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
// Can use an empty set, since event contains the info
jQuery().off( event );
return origFn.apply( this, arguments );
};
// Use same guid so caller can remove using origFn
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
},
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );
},
off: function( types, selector, fn ) {
var handleObj, type;
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
if ( typeof types === "object" ) {
// ( types-object [, selector] )
for ( type in types ) {
this.off( type, selector, types[ type ] );
}
return this;
}
if ( selector === false || typeof selector === "function" ) {
// ( types [, fn] )
fn = selector;
selector = undefined;
}
if ( fn === false ) {
fn = returnFalse;
}
return this.each(function() {
jQuery.event.remove( this, types, fn, selector );
});
},
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
triggerHandler: function( type, data ) {
var elem = this[0];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
});
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
if ( safeFrag.createElement ) {
while ( list.length ) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
rleadingWhitespace = /^\s+/,
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
// ##### BEGIN: MODIFIED BY SAP
// This fix solves performance issues of the IE regex engine
// rnoInnerhtml = /<(?:script|style|link)/i,
rnoInnerhtml = /(?:<script|<style|<link)/i,
// ##### END: MODIFIED BY SAP
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /^$|\/(?:java|ecma)script/i,
rscriptTypeMasked = /^true\/(.*)/,
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
// We have to close these tags to support XHTML (#13200)
wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
area: [ 1, "<map>", "</map>" ],
param: [ 1, "<object>", "</object>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
// unless wrapped in a div with non-breaking characters in front of it.
_default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
},
safeFragment = createSafeFragment( document ),
fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
function getAll( context, tag ) {
var elems, elem,
i = 0,
found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) :
undefined;
if ( !found ) {
for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
if ( !tag || jQuery.nodeName( elem, tag ) ) {
found.push( elem );
} else {
jQuery.merge( found, getAll( elem, tag ) );
}
}
}
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], found ) :
found;
}
// Used in buildFragment, fixes the defaultChecked property
function fixDefaultChecked( elem ) {
if ( rcheckableType.test( elem.type ) ) {
elem.defaultChecked = elem.checked;
}
}
// Support: IE<8
// Manipulating tables requires a tbody
function manipulationTarget( elem, content ) {
return jQuery.nodeName( elem, "table" ) &&
jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
elem.getElementsByTagName("tbody")[0] ||
elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
elem;
}
// Replace/restore the type attribute of script elements for safe DOM manipulation
function disableScript( elem ) {
elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
return elem;
}
function restoreScript( elem ) {
var match = rscriptTypeMasked.exec( elem.type );
if ( match ) {
elem.type = match[1];
} else {
elem.removeAttribute("type");
}
return elem;
}
// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var elem,
i = 0;
for ( ; (elem = elems[i]) != null; i++ ) {
jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
}
}
function cloneCopyEvent( src, dest ) {
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
return;
}
var type, i, l,
oldData = jQuery._data( src ),
curData = jQuery._data( dest, oldData ),
events = oldData.events;
if ( events ) {
delete curData.handle;
curData.events = {};
for ( type in events ) {
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ] );
}
}
}
// make the cloned public data object a copy from the original
if ( curData.data ) {
curData.data = jQuery.extend( {}, curData.data );
}
}
function fixCloneNodeIssues( src, dest ) {
var nodeName, e, data;
// We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) {
return;
}
nodeName = dest.nodeName.toLowerCase();
// IE6-8 copies events bound via attachEvent when using cloneNode.
if ( !support.noCloneEvent && dest[ jQuery.expando ] ) {
data = jQuery._data( dest );
for ( e in data.events ) {
jQuery.removeEvent( dest, e, data.handle );
}
// Event data gets referenced instead of copied if the expando gets copied too
dest.removeAttribute( jQuery.expando );
}
// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
if ( nodeName === "script" && dest.text !== src.text ) {
disableScript( dest ).text = src.text;
restoreScript( dest );
// IE6-10 improperly clones children of object elements using classid.
// IE10 throws NoModificationAllowedError if parent is null, #12132.
} else if ( nodeName === "object" ) {
if ( dest.parentNode ) {
dest.outerHTML = src.outerHTML;
}
// This path appears unavoidable for IE9. When cloning an object
// element in IE9, the outerHTML strategy above is not sufficient.
// If the src has innerHTML and the destination does not,
// copy the src.innerHTML into the dest.innerHTML. #10324
if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
dest.innerHTML = src.innerHTML;
}
} else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
// IE6-8 fails to persist the checked state of a cloned checkbox
// or radio button. Worse, IE6-7 fail to give the cloned element
// a checked appearance if the defaultChecked value isn't also set
dest.defaultChecked = dest.checked = src.checked;
// IE6-7 get confused and end up setting the value of a cloned
// checkbox/radio button to an empty string instead of "on"
if ( dest.value !== src.value ) {
dest.value = src.value;
}
// IE6-8 fails to return the selected option to the default selected
// state when cloning options
} else if ( nodeName === "option" ) {
dest.defaultSelected = dest.selected = src.defaultSelected;
// IE6-8 fails to set the defaultValue to the correct value when
// cloning other types of input fields
} else if ( nodeName === "input" || nodeName === "textarea" ) {
dest.defaultValue = src.defaultValue;
}
}
jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var destElements, node, clone, i, srcElements,
inPage = jQuery.contains( elem.ownerDocument, elem );
if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
clone = elem.cloneNode( true );
// IE<=8 does not properly clone detached, unknown element nodes
} else {
fragmentDiv.innerHTML = elem.outerHTML;
fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
}
if ( (!support.noCloneEvent || !support.noCloneChecked) &&
(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
destElements = getAll( clone );
srcElements = getAll( elem );
// Fix all IE cloning issues
for ( i = 0; (node = srcElements[i]) != null; ++i ) {
// Ensure that the destination node is not null; Fixes #9587
if ( destElements[i] ) {
fixCloneNodeIssues( node, destElements[i] );
}
}
}
// Copy the events from the original to the clone
if ( dataAndEvents ) {
if ( deepDataAndEvents ) {
srcElements = srcElements || getAll( elem );
destElements = destElements || getAll( clone );
for ( i = 0; (node = srcElements[i]) != null; i++ ) {
cloneCopyEvent( node, destElements[i] );
}
} else {
cloneCopyEvent( elem, clone );
}
}
// Preserve script evaluation history
destElements = getAll( clone, "script" );
if ( destElements.length > 0 ) {
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
}
destElements = srcElements = node = null;
// Return the cloned set
return clone;
},
buildFragment: function( elems, context, scripts, selection ) {
var j, elem, contains,
tmp, tag, tbody, wrap,
l = elems.length,
// Ensure a safe fragment
safe = createSafeFragment( context ),
nodes = [],
i = 0;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
// Convert non-html into a text node
} else if ( !rhtml.test( elem ) ) {
nodes.push( context.createTextNode( elem ) );
// Convert html into DOM nodes
} else {
tmp = tmp || safe.appendChild( context.createElement("div") );
// Deserialize a standard representation
tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
// Descend through wrappers to the right content
j = wrap[0];
while ( j-- ) {
tmp = tmp.lastChild;
}
// Manually add leading whitespace removed by IE
if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
}
// Remove IE's autoinserted <tbody> from table fragments
if ( !support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
elem = tag === "table" && !rtbody.test( elem ) ?
tmp.firstChild :
// String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !rtbody.test( elem ) ?
tmp :
0;
j = elem && elem.childNodes.length;
while ( j-- ) {
if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
elem.removeChild( tbody );
}
}
}
jQuery.merge( nodes, tmp.childNodes );
// Fix #12392 for WebKit and IE > 9
tmp.textContent = "";
// Fix #12392 for oldIE
while ( tmp.firstChild ) {
tmp.removeChild( tmp.firstChild );
}
// Remember the top-level container for proper cleanup
tmp = safe.lastChild;
}
}
}
// Fix #11356: Clear elements from fragment
if ( tmp ) {
safe.removeChild( tmp );
}
// Reset defaultChecked for any radios and checkboxes
// about to be appended to the DOM in IE 6/7 (#8060)
if ( !support.appendChecked ) {
jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
}
i = 0;
while ( (elem = nodes[ i++ ]) ) {
// #4087 - If origin and destination elements are the same, and this is
// that element, do not do anything
if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
continue;
}
contains = jQuery.contains( elem.ownerDocument, elem );
// Append to fragment
tmp = getAll( safe.appendChild( elem ), "script" );
// Preserve script evaluation history
if ( contains ) {
setGlobalEval( tmp );
}
// Capture executables
if ( scripts ) {
j = 0;
while ( (elem = tmp[ j++ ]) ) {
if ( rscriptType.test( elem.type || "" ) ) {
scripts.push( elem );
}
}
}
}
tmp = null;
return safe;
},
cleanData: function( elems, /* internal */ acceptData ) {
var elem, type, id, data,
i = 0,
internalKey = jQuery.expando,
cache = jQuery.cache,
deleteExpando = support.deleteExpando,
special = jQuery.event.special;
for ( ; (elem = elems[i]) != null; i++ ) {
if ( acceptData || jQuery.acceptData( elem ) ) {
id = elem[ internalKey ];
data = id && cache[ id ];
if ( data ) {
if ( data.events ) {
for ( type in data.events ) {
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
// This is a shortcut to avoid jQuery.event.remove's overhead
} else {
jQuery.removeEvent( elem, type, data.handle );
}
}
}
// Remove cache only if it was not already removed by jQuery.event.remove
if ( cache[ id ] ) {
delete cache[ id ];
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
if ( deleteExpando ) {
delete elem[ internalKey ];
} else if ( typeof elem.removeAttribute !== strundefined ) {
elem.removeAttribute( internalKey );
} else {
elem[ internalKey ] = null;
}
deletedIds.push( id );
}
}
}
}
}
});
jQuery.fn.extend({
text: function( value ) {
return access( this, function( value ) {
return value === undefined ?
jQuery.text( this ) :
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
}, null, value, arguments.length );
},
append: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.appendChild( elem );
}
});
},
prepend: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.insertBefore( elem, target.firstChild );
}
});
},
before: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
this.parentNode.insertBefore( elem, this );
}
});
},
after: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
this.parentNode.insertBefore( elem, this.nextSibling );
}
});
},
remove: function( selector, keepData /* Internal Use Only */ ) {
var elem,
elems = selector ? jQuery.filter( selector, this ) : this,
i = 0;
for ( ; (elem = elems[i]) != null; i++ ) {
if ( !keepData && elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem ) );
}
if ( elem.parentNode ) {
if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
setGlobalEval( getAll( elem, "script" ) );
}
elem.parentNode.removeChild( elem );
}
}
return this;
},
empty: function() {
var elem,
i = 0;
for ( ; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
}
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
// If this is a select, ensure that it displays empty (#12336)
// Support: IE<9
if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
elem.options.length = 0;
}
}
return this;
},
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map(function() {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
html: function( value ) {
return access( this, function( value ) {
var elem = this[ 0 ] || {},
i = 0,
l = this.length;
if ( value === undefined ) {
return elem.nodeType === 1 ?
elem.innerHTML.replace( rinlinejQuery, "" ) :
undefined;
}
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
( support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
value = value.replace( rxhtmlTag, "<$1></$2>" );
try {
for (; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
elem = this[i] || {};
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
elem.innerHTML = value;
}
}
elem = 0;
// If using innerHTML throws an exception, use the fallback method
} catch(e) {}
}
if ( elem ) {
this.empty().append( value );
}
}, null, value, arguments.length );
},
replaceWith: function() {
var arg = arguments[ 0 ];
// Make the changes, replacing each context element with the new content
this.domManip( arguments, function( elem ) {
arg = this.parentNode;
jQuery.cleanData( getAll( this ) );
if ( arg ) {
arg.replaceChild( elem, this );
}
});
// Force removal if there was no new content (e.g., from empty arguments)
return arg && (arg.length || arg.nodeType) ? this : this.remove();
},
detach: function( selector ) {
return this.remove( selector, true );
},
domManip: function( args, callback ) {
// Flatten any nested arrays
args = concat.apply( [], args );
var first, node, hasScripts,
scripts, doc, fragment,
i = 0,
l = this.length,
set = this,
iNoClone = l - 1,
value = args[0],
isFunction = jQuery.isFunction( value );
// We can't cloneNode fragments that contain checked, in WebKit
if ( isFunction ||
( l > 1 && typeof value === "string" &&
!support.checkClone && rchecked.test( value ) ) ) {
return this.each(function( index ) {
var self = set.eq( index );
if ( isFunction ) {
args[0] = value.call( this, index, self.html() );
}
self.domManip( args, callback );
});
}
if ( l ) {
fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
first = fragment.firstChild;
if ( fragment.childNodes.length === 1 ) {
fragment = first;
}
if ( first ) {
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
hasScripts = scripts.length;
// Use the original fragment for the last item instead of the first because it can end up
// being emptied incorrectly in certain situations (#8070).
for ( ; i < l; i++ ) {
node = fragment;
if ( i !== iNoClone ) {
node = jQuery.clone( node, true, true );
// Keep references to cloned scripts for later restoration
if ( hasScripts ) {
jQuery.merge( scripts, getAll( node, "script" ) );
}
}
callback.call( this[i], node, i );
}
if ( hasScripts ) {
doc = scripts[ scripts.length - 1 ].ownerDocument;
// Reenable scripts
jQuery.map( scripts, restoreScript );
// Evaluate executable scripts on first document insertion
for ( i = 0; i < hasScripts; i++ ) {
node = scripts[ i ];
if ( rscriptType.test( node.type || "" ) &&
!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
if ( node.src ) {
// Optional AJAX dependency, but won't run scripts if not present
if ( jQuery._evalUrl ) {
jQuery._evalUrl( node.src );
}
} else {
jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
}
}
}
}
// Fix #11809: Avoid leaking memory
fragment = first = null;
}
}
return this;
}
});
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
var elems,
i = 0,
ret = [],
insert = jQuery( selector ),
last = insert.length - 1;
for ( ; i <= last; i++ ) {
elems = i === last ? this : this.clone(true);
jQuery( insert[i] )[ original ]( elems );
// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
push.apply( ret, elems.get() );
}
return this.pushStack( ret );
};
});
var iframe,
elemdisplay = {};
/**
* Retrieve the actual display of a element
* @param {String} name nodeName of the element
* @param {Object} doc Document object
*/
// Called only from within defaultDisplay
function actualDisplay( name, doc ) {
var style,
elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
// getDefaultComputedStyle might be reliably used only on attached element
display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
// Use of this method is a temporary fix (more like optmization) until something better comes along,
// since it was removed from specification and supported only in FF
style.display : jQuery.css( elem[ 0 ], "display" );
// We don't have any data stored on the element,
// so use "detach" method as fast way to get rid of the element
elem.detach();
return display;
}
/**
* Try to determine the default display value of an element
* @param {String} nodeName
*/
function defaultDisplay( nodeName ) {
var doc = document,
display = elemdisplay[ nodeName ];
if ( !display ) {
display = actualDisplay( nodeName, doc );
// If the simple way fails, read from inside an iframe
if ( display === "none" || !display ) {
// Use the already-created iframe if possible
iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
doc = ( iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument ).document;
// Support: IE
doc.write();
doc.close();
display = actualDisplay( nodeName, doc );
iframe.detach();
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
}
return display;
}
(function() {
var shrinkWrapBlocksVal;
support.shrinkWrapBlocks = function() {
if ( shrinkWrapBlocksVal != null ) {
return shrinkWrapBlocksVal;
}
// Will be changed later if needed.
shrinkWrapBlocksVal = false;
// Minified: var b,c,d
var div, body, container;
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body || !body.style ) {
// Test fired too early or in an unsupported environment, exit.
return;
}
// Setup
div = document.createElement( "div" );
container = document.createElement( "div" );
container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
body.appendChild( container ).appendChild( div );
// Support: IE6
// Check if elements with layout shrink-wrap their children
if ( typeof div.style.zoom !== strundefined ) {
// Reset CSS: box-sizing; display; margin; border
div.style.cssText =
// Support: Firefox<29, Android 2.3
// Vendor-prefix box-sizing
"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
"box-sizing:content-box;display:block;margin:0;border:0;" +
"padding:1px;width:1px;zoom:1";
div.appendChild( document.createElement( "div" ) ).style.width = "5px";
shrinkWrapBlocksVal = div.offsetWidth !== 3;
}
body.removeChild( container );
return shrinkWrapBlocksVal;
};
})();
var rmargin = (/^margin/);
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
var getStyles, curCSS,
rposition = /^(top|right|bottom|left)$/;
if ( window.getComputedStyle ) {
getStyles = function( elem ) {
return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
};
curCSS = function( elem, name, computed ) {
var width, minWidth, maxWidth, ret,
style = elem.style;
computed = computed || getStyles( elem );
// getPropertyValue is only needed for .css('filter') in IE9, see #12537
ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
if ( computed ) {
if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
ret = jQuery.style( elem, name );
}
// A tribute to the "awesome hack by Dean Edwards"
// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
// Remember the original values
width = style.width;
minWidth = style.minWidth;
maxWidth = style.maxWidth;
// Put in the new values to get a computed value out
style.minWidth = style.maxWidth = style.width = ret;
ret = computed.width;
// Revert the changed values
style.width = width;
style.minWidth = minWidth;
style.maxWidth = maxWidth;
}
}
// Support: IE
// IE returns zIndex value as an integer.
return ret === undefined ?
ret :
ret + "";
};
} else if ( document.documentElement.currentStyle ) {
getStyles = function( elem ) {
return elem.currentStyle;
};
curCSS = function( elem, name, computed ) {
var left, rs, rsLeft, ret,
style = elem.style;
computed = computed || getStyles( elem );
ret = computed ? computed[ name ] : undefined;
// Avoid setting ret to empty string here
// so we don't default to auto
if ( ret == null && style && style[ name ] ) {
ret = style[ name ];
}
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
// but not position css attributes, as those are proportional to the parent element instead
// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
// Remember the original values
left = style.left;
rs = elem.runtimeStyle;
rsLeft = rs && rs.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
rs.left = elem.currentStyle.left;
}
style.left = name === "fontSize" ? "1em" : ret;
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
if ( rsLeft ) {
rs.left = rsLeft;
}
}
// Support: IE
// IE returns zIndex value as an integer.
return ret === undefined ?
ret :
ret + "" || "auto";
};
}
function addGetHookIf( conditionFn, hookFn ) {
// Define the hook, we'll check on the first run if it's really needed.
return {
get: function() {
var condition = conditionFn();
if ( condition == null ) {
// The test was not ready at this point; screw the hook this time
// but check again when needed next time.
return;
}
if ( condition ) {
// Hook not needed (or it's not possible to use it due to missing dependency),
// remove it.
// Since there are no other hooks for marginRight, remove the whole object.
delete this.get;
return;
}
// Hook needed; redefine it so that the support test is not executed again.
return (this.get = hookFn).apply( this, arguments );
}
};
}
(function() {
// Minified: var b,c,d,e,f,g, h,i
var div, style, a, pixelPositionVal, boxSizingReliableVal,
reliableHiddenOffsetsVal, reliableMarginRightVal;
// Setup
div = document.createElement( "div" );
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
a = div.getElementsByTagName( "a" )[ 0 ];
style = a && a.style;
// Finish early in limited (non-browser) environments
if ( !style ) {
return;
}
style.cssText = "float:left;opacity:.5";
// Support: IE<9
// Make sure that element opacity exists (as opposed to filter)
support.opacity = style.opacity === "0.5";
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
support.cssFloat = !!style.cssFloat;
div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box";
// Support: Firefox<29, Android 2.3
// Vendor-prefix box-sizing
support.boxSizing = style.boxSizing === "" || style.MozBoxSizing === "" ||
style.WebkitBoxSizing === "";
jQuery.extend(support, {
reliableHiddenOffsets: function() {
if ( reliableHiddenOffsetsVal == null ) {
computeStyleTests();
}
return reliableHiddenOffsetsVal;
},
boxSizingReliable: function() {
if ( boxSizingReliableVal == null ) {
computeStyleTests();
}
return boxSizingReliableVal;
},
pixelPosition: function() {
if ( pixelPositionVal == null ) {
computeStyleTests();
}
return pixelPositionVal;
},
// Support: Android 2.3
reliableMarginRight: function() {
if ( reliableMarginRightVal == null ) {
computeStyleTests();
}
return reliableMarginRightVal;
}
});
function computeStyleTests() {
// Minified: var b,c,d,j
var div, body, container, contents;
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body || !body.style ) {
// Test fired too early or in an unsupported environment, exit.
return;
}
// Setup
div = document.createElement( "div" );
container = document.createElement( "div" );
container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
body.appendChild( container ).appendChild( div );
div.style.cssText =
// Support: Firefox<29, Android 2.3
// Vendor-prefix box-sizing
"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
"border:1px;padding:1px;width:4px;position:absolute";
// Support: IE<9
// Assume reasonable values in the absence of getComputedStyle
pixelPositionVal = boxSizingReliableVal = false;
reliableMarginRightVal = true;
// Check for getComputedStyle so that this code is not run in IE<9.
if ( window.getComputedStyle ) {
pixelPositionVal = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
boxSizingReliableVal =
( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
// Support: Android 2.3
// Div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container (#3333)
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
contents = div.appendChild( document.createElement( "div" ) );
// Reset CSS: box-sizing; display; margin; border; padding
contents.style.cssText = div.style.cssText =
// Support: Firefox<29, Android 2.3
// Vendor-prefix box-sizing
"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
contents.style.marginRight = contents.style.width = "0";
div.style.width = "1px";
reliableMarginRightVal =
!parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight );
}
// Support: IE8
// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
contents = div.getElementsByTagName( "td" );
contents[ 0 ].style.cssText = "margin:0;border:0;padding:0;display:none";
reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
if ( reliableHiddenOffsetsVal ) {
contents[ 0 ].style.display = "";
contents[ 1 ].style.display = "none";
reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
}
body.removeChild( container );
}
})();
// A method for quickly swapping in/out CSS properties to get correct calculations.
jQuery.swap = function( elem, options, callback, args ) {
var ret, name,
old = {};
// Remember the old values, and insert the new ones
for ( name in options ) {
old[ name ] = elem.style[ name ];
elem.style[ name ] = options[ name ];
}
ret = callback.apply( elem, args || [] );
// Revert the old values
for ( name in options ) {
elem.style[ name ] = old[ name ];
}
return ret;
};
var
ralpha = /alpha\([^)]*\)/i,
ropacity = /opacity\s*=\s*([^)]*)/,
// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = {
letterSpacing: "0",
fontWeight: "400"
},
cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
// return a css property mapped to a potentially vendor prefixed property
function vendorPropName( style, name ) {
// shortcut for names that are not vendor prefixed
if ( name in style ) {
return name;
}
// check for vendor prefixed names
var capName = name.charAt(0).toUpperCase() + name.slice(1),
origName = name,
i = cssPrefixes.length;
while ( i-- ) {
name = cssPrefixes[ i ] + capName;
if ( name in style ) {
return name;
}
}
return origName;
}
function showHide( elements, show ) {
var display, elem, hidden,
values = [],
index = 0,
length = elements.length;
for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
values[ index ] = jQuery._data( elem, "olddisplay" );
display = elem.style.display;
if ( show ) {
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !values[ index ] && display === "none" ) {
elem.style.display = "";
}
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( elem.style.display === "" && isHidden( elem ) ) {
values[ index ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
} else {
hidden = isHidden( elem );
if ( display && display !== "none" || !hidden ) {
jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
}
}
}
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( index = 0; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
}
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
elem.style.display = show ? values[ index ] || "" : "none";
}
}
return elements;
}
function setPositiveNumber( elem, value, subtract ) {
var matches = rnumsplit.exec( value );
return matches ?
// Guard against undefined "subtract", e.g., when used as in cssHooks
Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
value;
}
function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
var i = extra === ( isBorderBox ? "border" : "content" ) ?
// If we already have the right measurement, avoid augmentation
4 :
// Otherwise initialize for horizontal or vertical properties
name === "width" ? 1 : 0,
val = 0;
for ( ; i < 4; i += 2 ) {
// both box models exclude margin, so add it if we want it
if ( extra === "margin" ) {
val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
}
if ( isBorderBox ) {
// border-box includes padding, so remove it if we want content
if ( extra === "content" ) {
val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
}
// at this point, extra isn't border nor margin, so remove border
if ( extra !== "margin" ) {
val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
}
} else {
// at this point, extra isn't content, so add padding
val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
// at this point, extra isn't content nor padding, so add border
if ( extra !== "padding" ) {
val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
}
}
}
return val;
}
function getWidthOrHeight( elem, name, extra ) {
// Start with offset property, which is equivalent to the border-box value
var valueIsBorderBox = true,
val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
styles = getStyles( elem ),
isBorderBox = support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
// some non-html elements return undefined for offsetWidth, so check for null/undefined
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
if ( val <= 0 || val == null ) {
// Fall back to computed then uncomputed css if necessary
val = curCSS( elem, name, styles );
if ( val < 0 || val == null ) {
val = elem.style[ name ];
}
// Computed unit is not pixels. Stop here and return.
if ( rnumnonpx.test(val) ) {
return val;
}
// we need the check for style in case a browser which returns unreliable values
// for getComputedStyle silently falls back to the reliable elem.style
valueIsBorderBox = isBorderBox && ( support.boxSizingReliable() || val === elem.style[ name ] );
// Normalize "", auto, and prepare for extra
val = parseFloat( val ) || 0;
}
// use the active box-sizing model to add/subtract irrelevant styles
return ( val +
augmentWidthOrHeight(
elem,
name,
extra || ( isBorderBox ? "border" : "content" ),
valueIsBorderBox,
styles
)
) + "px";
}
jQuery.extend({
// Add in style property hooks for overriding the default
// behavior of getting and setting a style property
cssHooks: {
opacity: {
get: function( elem, computed ) {
if ( computed ) {
// We should always get a number back from opacity
var ret = curCSS( elem, "opacity" );
return ret === "" ? "1" : ret;
}
}
}
},
// Don't automatically add "px" to these possibly-unitless properties
cssNumber: {
"columnCount": true,
"fillOpacity": true,
"flexGrow": true,
"flexShrink": true,
"fontWeight": true,
"lineHeight": true,
"opacity": true,
"order": true,
"orphans": true,
"widows": true,
"zIndex": true,
"zoom": true
},
// Add in properties whose names you wish to fix before
// setting or getting the value
cssProps: {
// normalize float css property
"float": support.cssFloat ? "cssFloat" : "styleFloat"
},
// Get and set the style property on a DOM Node
style: function( elem, name, value, extra ) {
// Don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return;
}
// Make sure that we're working with the right name
var ret, type, hooks,
origName = jQuery.camelCase( name ),
style = elem.style;
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
// gets hook for the prefixed version
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// Check if we're setting a value
if ( value !== undefined ) {
type = typeof value;
// convert relative number strings (+= or -=) to relative numbers. #7345
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
// Fixes bug #9237
type = "number";
}
// Make sure that null and NaN values aren't set. See: #7116
if ( value == null || value !== value ) {
return;
}
// If a number was passed in, add 'px' to the (except for certain CSS properties)
if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px";
}
// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
// but it would mean to define eight (for every problematic property) identical functions
if ( !support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
style[ name ] = "inherit";
}
// If a hook was provided, use that value, otherwise just set the specified value
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
// Support: IE
// Swallow errors from 'invalid' CSS values (#5509)
try {
style[ name ] = value;
} catch(e) {}
}
} else {
// If a hook was provided get the non-computed value from there
if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
return ret;
}
// Otherwise just get the value from the style object
return style[ name ];
}
},
css: function( elem, name, extra, styles ) {
var num, val, hooks,
origName = jQuery.camelCase( name );
// Make sure that we're working with the right name
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
// gets hook for the prefixed version
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// If a hook was provided get the computed value from there
if ( hooks && "get" in hooks ) {
val = hooks.get( elem, true, extra );
}
// Otherwise, if a way to get the computed value exists, use that
if ( val === undefined ) {
val = curCSS( elem, name, styles );
}
//convert "normal" to computed value
if ( val === "normal" && name in cssNormalTransform ) {
val = cssNormalTransform[ name ];
}
// Return, converting to number if forced or a qualifier was provided and val looks numeric
if ( extra === "" || extra ) {
num = parseFloat( val );
return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
}
return val;
}
});
jQuery.each([ "height", "width" ], function( i, name ) {
jQuery.cssHooks[ name ] = {
get: function( elem, computed, extra ) {
if ( computed ) {
// certain elements can have dimension info if we invisibly show them
// however, it must have a current display style that would benefit from this
return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
jQuery.swap( elem, cssShow, function() {
return getWidthOrHeight( elem, name, extra );
}) :
getWidthOrHeight( elem, name, extra );
}
},
set: function( elem, value, extra ) {
var styles = extra && getStyles( elem );
return setPositiveNumber( elem, value, extra ?
augmentWidthOrHeight(
elem,
name,
extra,
support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
styles
) : 0
);
}
};
});
if ( !support.opacity ) {
jQuery.cssHooks.opacity = {
get: function( elem, computed ) {
// IE uses filters for opacity
return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
computed ? "1" : "";
},
set: function( elem, value ) {
var style = elem.style,
currentStyle = elem.currentStyle,
opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
filter = currentStyle && currentStyle.filter || style.filter || "";
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
style.zoom = 1;
// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
// if value === "", then remove inline opacity #12685
if ( ( value >= 1 || value === "" ) &&
jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
style.removeAttribute ) {
// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
// if "filter:" is present at all, clearType is disabled, we want to avoid this
// style.removeAttribute is IE Only, but so apparently is this code path...
style.removeAttribute( "filter" );
// if there is no filter style applied in a css rule or unset inline opacity, we are done
if ( value === "" || currentStyle && !currentStyle.filter ) {
return;
}
}
// otherwise, set new filter values
style.filter = ralpha.test( filter ) ?
filter.replace( ralpha, opacity ) :
filter + " " + opacity;
}
};
}
jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
function( elem, computed ) {
if ( computed ) {
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
// Work around by temporarily setting element display to inline-block
return jQuery.swap( elem, { "display": "inline-block" },
curCSS, [ elem, "marginRight" ] );
}
}
);
// These hooks are used by animate to expand properties
jQuery.each({
margin: "",
padding: "",
border: "Width"
}, function( prefix, suffix ) {
jQuery.cssHooks[ prefix + suffix ] = {
expand: function( value ) {
var i = 0,
expanded = {},
// assumes a single number if not a string
parts = typeof value === "string" ? value.split(" ") : [ value ];
for ( ; i < 4; i++ ) {
expanded[ prefix + cssExpand[ i ] + suffix ] =
parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
}
return expanded;
}
};
if ( !rmargin.test( prefix ) ) {
jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
}
});
jQuery.fn.extend({
css: function( name, value ) {
return access( this, function( elem, name, value ) {
var styles, len,
map = {},
i = 0;
if ( jQuery.isArray( name ) ) {
styles = getStyles( elem );
len = name.length;
for ( ; i < len; i++ ) {
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
}
return map;
}
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
}, name, value, arguments.length > 1 );
},
show: function() {
return showHide( this, true );
},
hide: function() {
return showHide( this );
},
toggle: function( state ) {
if ( typeof state === "boolean" ) {
return state ? this.show() : this.hide();
}
return this.each(function() {
if ( isHidden( this ) ) {
jQuery( this ).show();
} else {
jQuery( this ).hide();
}
});
}
});
function Tween( elem, options, prop, end, easing ) {
return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween;
Tween.prototype = {
constructor: Tween,
init: function( elem, options, prop, end, easing, unit ) {
this.elem = elem;
this.prop = prop;
this.easing = easing || "swing";
this.options = options;
this.start = this.now = this.cur();
this.end = end;
this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
},
cur: function() {
var hooks = Tween.propHooks[ this.prop ];
return hooks && hooks.get ?
hooks.get( this ) :
Tween.propHooks._default.get( this );
},
run: function( percent ) {
var eased,
hooks = Tween.propHooks[ this.prop ];
if ( this.options.duration ) {
this.pos = eased = jQuery.easing[ this.easing ](
percent, this.options.duration * percent, 0, 1, this.options.duration
);
} else {
this.pos = eased = percent;
}
this.now = ( this.end - this.start ) * eased + this.start;
if ( this.options.step ) {
this.options.step.call( this.elem, this.now, this );
}
if ( hooks && hooks.set ) {
hooks.set( this );
} else {
Tween.propHooks._default.set( this );
}
return this;
}
};
Tween.prototype.init.prototype = Tween.prototype;
Tween.propHooks = {
_default: {
get: function( tween ) {
var result;
if ( tween.elem[ tween.prop ] != null &&
(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
return tween.elem[ tween.prop ];
}
// passing an empty string as a 3rd parameter to .css will automatically
// attempt a parseFloat and fallback to a string if the parse fails
// so, simple values such as "10px" are parsed to Float.
// complex values such as "rotate(1rad)" are returned as is.
result = jQuery.css( tween.elem, tween.prop, "" );
// Empty strings, null, undefined and "auto" are converted to 0.
return !result || result === "auto" ? 0 : result;
},
set: function( tween ) {
// use step hook for back compat - use cssHook if its there - use .style if its
// available and use plain properties where available
if ( jQuery.fx.step[ tween.prop ] ) {
jQuery.fx.step[ tween.prop ]( tween );
} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
} else {
tween.elem[ tween.prop ] = tween.now;
}
}
}
};
// Support: IE <=9
// Panic based approach to setting things on disconnected nodes
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
set: function( tween ) {
if ( tween.elem.nodeType && tween.elem.parentNode ) {
tween.elem[ tween.prop ] = tween.now;
}
}
};
jQuery.easing = {
linear: function( p ) {
return p;
},
swing: function( p ) {
return 0.5 - Math.cos( p * Math.PI ) / 2;
}
};
jQuery.fx = Tween.prototype.init;
// Back Compat <1.8 extension point
jQuery.fx.step = {};
var
fxNow, timerId,
rfxtypes = /^(?:toggle|show|hide)$/,
rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
rrun = /queueHooks$/,
animationPrefilters = [ defaultPrefilter ],
tweeners = {
"*": [ function( prop, value ) {
var tween = this.createTween( prop, value ),
target = tween.cur(),
parts = rfxnum.exec( value ),
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
// Starting value computation is required for potential unit mismatches
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
scale = 1,
maxIterations = 20;
if ( start && start[ 3 ] !== unit ) {
// Trust units reported by jQuery.css
unit = unit || start[ 3 ];
// Make sure we update the tween properties later on
parts = parts || [];
// Iteratively approximate from a nonzero starting point
start = +target || 1;
do {
// If previous iteration zeroed out, double until we get *something*
// Use a string for doubling factor so we don't accidentally see scale as unchanged below
scale = scale || ".5";
// Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit );
// Update scale, tolerating zero or NaN from tween.cur()
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
}
// Update tween properties
if ( parts ) {
start = tween.start = +start || +target || 0;
tween.unit = unit;
// If a +=/-= token was provided, we're doing a relative animation
tween.end = parts[ 1 ] ?
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+parts[ 2 ];
}
return tween;
} ]
};
// Animations created synchronously will run synchronously
function createFxNow() {
setTimeout(function() {
fxNow = undefined;
});
return ( fxNow = jQuery.now() );
}
// Generate parameters to create a standard animation
function genFx( type, includeWidth ) {
var which,
attrs = { height: type },
i = 0;
// if we include width, step value is 1 to do all cssExpand values,
// if we don't include width, step value is 2 to skip over Left and Right
includeWidth = includeWidth ? 1 : 0;
for ( ; i < 4 ; i += 2 - includeWidth ) {
which = cssExpand[ i ];
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
}
if ( includeWidth ) {
attrs.opacity = attrs.width = type;
}
return attrs;
}
function createTween( value, prop, animation ) {
var tween,
collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0,
length = collection.length;
for ( ; index < length; index++ ) {
if ( (tween = collection[ index ].call( animation, prop, value )) ) {
// we're done with this property
return tween;
}
}
}
function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */
var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
anim = this,
orig = {},
style = elem.style,
hidden = elem.nodeType && isHidden( elem ),
dataShow = jQuery._data( elem, "fxshow" );
// handle queue: false promises
if ( !opts.queue ) {
hooks = jQuery._queueHooks( elem, "fx" );
if ( hooks.unqueued == null ) {
hooks.unqueued = 0;
oldfire = hooks.empty.fire;
hooks.empty.fire = function() {
if ( !hooks.unqueued ) {
oldfire();
}
};
}
hooks.unqueued++;
anim.always(function() {
// doing this makes sure that the complete handler will be called
// before this completes
anim.always(function() {
hooks.unqueued--;
if ( !jQuery.queue( elem, "fx" ).length ) {
hooks.empty.fire();
}
});
});
}
// height/width overflow pass
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
// Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE does not
// change the overflow attribute when overflowX and
// overflowY are set to the same value
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
// Set display property to inline-block for height/width
// animations on inline elements that are having width/height animated
display = jQuery.css( elem, "display" );
// Test default display if display is currently "none"
checkDisplay = display === "none" ?
jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
// inline-level elements accept inline-block;
// block-level elements need to be inline with layout
if ( !support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
style.display = "inline-block";
} else {
style.zoom = 1;
}
}
}
if ( opts.overflow ) {
style.overflow = "hidden";
if ( !support.shrinkWrapBlocks() ) {
anim.always(function() {
style.overflow = opts.overflow[ 0 ];
style.overflowX = opts.overflow[ 1 ];
style.overflowY = opts.overflow[ 2 ];
});
}
}
// show/hide pass
for ( prop in props ) {
value = props[ prop ];
if ( rfxtypes.exec( value ) ) {
delete props[ prop ];
toggle = toggle || value === "toggle";
if ( value === ( hidden ? "hide" : "show" ) ) {
// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
hidden = true;
} else {
continue;
}
}
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
// Any non-fx value stops us from restoring the original display value
} else {
display = undefined;
}
}
if ( !jQuery.isEmptyObject( orig ) ) {
if ( dataShow ) {
if ( "hidden" in dataShow ) {
hidden = dataShow.hidden;
}
} else {
dataShow = jQuery._data( elem, "fxshow", {} );
}
// store state if its toggle - enables .stop().toggle() to "reverse"
if ( toggle ) {
dataShow.hidden = !hidden;
}
if ( hidden ) {
jQuery( elem ).show();
} else {
anim.done(function() {
jQuery( elem ).hide();
});
}
anim.done(function() {
var prop;
jQuery._removeData( elem, "fxshow" );
for ( prop in orig ) {
jQuery.style( elem, prop, orig[ prop ] );
}
});
for ( prop in orig ) {
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
if ( !( prop in dataShow ) ) {
dataShow[ prop ] = tween.start;
if ( hidden ) {
tween.end = tween.start;
tween.start = prop === "width" || prop === "height" ? 1 : 0;
}
}
}
// If this is a noop like .hide().hide(), restore an overwritten display value
} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
style.display = display;
}
}
function propFilter( props, specialEasing ) {
var index, name, easing, value, hooks;
// camelCase, specialEasing and expand cssHook pass
for ( index in props ) {
name = jQuery.camelCase( index );
easing = specialEasing[ name ];
value = props[ index ];
if ( jQuery.isArray( value ) ) {
easing = value[ 1 ];
value = props[ index ] = value[ 0 ];
}
if ( index !== name ) {
props[ name ] = value;
delete props[ index ];
}
hooks = jQuery.cssHooks[ name ];
if ( hooks && "expand" in hooks ) {
value = hooks.expand( value );
delete props[ name ];
// not quite $.extend, this wont overwrite keys already present.
// also - reusing 'index' from above because we have the correct "name"
for ( index in value ) {
if ( !( index in props ) ) {
props[ index ] = value[ index ];
specialEasing[ index ] = easing;
}
}
} else {
specialEasing[ name ] = easing;
}
}
}
function Animation( elem, properties, options ) {
var result,
stopped,
index = 0,
length = animationPrefilters.length,
deferred = jQuery.Deferred().always( function() {
// don't match elem in the :animated selector
delete tick.elem;
}),
tick = function() {
if ( stopped ) {
return false;
}
var currentTime = fxNow || createFxNow(),
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
temp = remaining / animation.duration || 0,
percent = 1 - temp,
index = 0,
length = animation.tweens.length;
for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( percent );
}
deferred.notifyWith( elem, [ animation, percent, remaining ]);
if ( percent < 1 && length ) {
return remaining;
} else {
deferred.resolveWith( elem, [ animation ] );
return false;
}
},
animation = deferred.promise({
elem: elem,
props: jQuery.extend( {}, properties ),
opts: jQuery.extend( true, { specialEasing: {} }, options ),
originalProperties: properties,
originalOptions: options,
startTime: fxNow || createFxNow(),
duration: options.duration,
tweens: [],
createTween: function( prop, end ) {
var tween = jQuery.Tween( elem, animation.opts, prop, end,
animation.opts.specialEasing[ prop ] || animation.opts.easing );
animation.tweens.push( tween );
return tween;
},
stop: function( gotoEnd ) {
var index = 0,
// if we are going to the end, we want to run all the tweens
// otherwise we skip this part
length = gotoEnd ? animation.tweens.length : 0;
if ( stopped ) {
return this;
}
stopped = true;
for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( 1 );
}
// resolve when we played the last frame
// otherwise, reject
if ( gotoEnd ) {
deferred.resolveWith( elem, [ animation, gotoEnd ] );
} else {
deferred.rejectWith( elem, [ animation, gotoEnd ] );
}
return this;
}
}),
props = animation.props;
propFilter( props, animation.opts.specialEasing );
for ( ; index < length ; index++ ) {
result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
if ( result ) {
return result;
}
}
jQuery.map( props, createTween, animation );
if ( jQuery.isFunction( animation.opts.start ) ) {
animation.opts.start.call( elem, animation );
}
jQuery.fx.timer(
jQuery.extend( tick, {
elem: elem,
anim: animation,
queue: animation.opts.queue
})
);
// attach callbacks from options
return animation.progress( animation.opts.progress )
.done( animation.opts.done, animation.opts.complete )
.fail( animation.opts.fail )
.always( animation.opts.always );
}
jQuery.Animation = jQuery.extend( Animation, {
tweener: function( props, callback ) {
if ( jQuery.isFunction( props ) ) {
callback = props;
props = [ "*" ];
} else {
props = props.split(" ");
}
var prop,
index = 0,
length = props.length;
for ( ; index < length ; index++ ) {
prop = props[ index ];
tweeners[ prop ] = tweeners[ prop ] || [];
tweeners[ prop ].unshift( callback );
}
},
prefilter: function( callback, prepend ) {
if ( prepend ) {
animationPrefilters.unshift( callback );
} else {
animationPrefilters.push( callback );
}
}
});
jQuery.speed = function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
};
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
// normalize opt.queue - true/undefined/null -> "fx"
if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx";
}
// Queueing
opt.old = opt.complete;
opt.complete = function() {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
}
if ( opt.queue ) {
jQuery.dequeue( this, opt.queue );
}
};
return opt;
};
jQuery.fn.extend({
fadeTo: function( speed, to, easing, callback ) {
// show any hidden elements after setting opacity to 0
return this.filter( isHidden ).css( "opacity", 0 ).show()
// animate to the value specified
.end().animate({ opacity: to }, speed, easing, callback );
},
animate: function( prop, speed, easing, callback ) {
var empty = jQuery.isEmptyObject( prop ),
optall = jQuery.speed( speed, easing, callback ),
doAnimation = function() {
// Operate on a copy of prop so per-property easing won't be lost
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
// Empty animations, or finishing resolves immediately
if ( empty || jQuery._data( this, "finish" ) ) {
anim.stop( true );
}
};
doAnimation.finish = doAnimation;
return empty || optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},
stop: function( type, clearQueue, gotoEnd ) {
var stopQueue = function( hooks ) {
var stop = hooks.stop;
delete hooks.stop;
stop( gotoEnd );
};
if ( typeof type !== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
type = undefined;
}
if ( clearQueue && type !== false ) {
this.queue( type || "fx", [] );
}
return this.each(function() {
var dequeue = true,
index = type != null && type + "queueHooks",
timers = jQuery.timers,
data = jQuery._data( this );
if ( index ) {
if ( data[ index ] && data[ index ].stop ) {
stopQueue( data[ index ] );
}
} else {
for ( index in data ) {
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
stopQueue( data[ index ] );
}
}
}
for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
timers[ index ].anim.stop( gotoEnd );
dequeue = false;
timers.splice( index, 1 );
}
}
// start the next in the queue if the last step wasn't forced
// timers currently will call their complete callbacks, which will dequeue
// but only if they were gotoEnd
if ( dequeue || !gotoEnd ) {
jQuery.dequeue( this, type );
}
});
},
finish: function( type ) {
if ( type !== false ) {
type = type || "fx";
}
return this.each(function() {
var index,
data = jQuery._data( this ),
queue = data[ type + "queue" ],
hooks = data[ type + "queueHooks" ],
timers = jQuery.timers,
length = queue ? queue.length : 0;
// enable finishing flag on private data
data.finish = true;
// empty the queue first
jQuery.queue( this, type, [] );
if ( hooks && hooks.stop ) {
hooks.stop.call( this, true );
}
// look for any active animations, and finish them
for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
timers[ index ].anim.stop( true );
timers.splice( index, 1 );
}
}
// look for any animations in the old queue and finish them
for ( index = 0; index < length; index++ ) {
if ( queue[ index ] && queue[ index ].finish ) {
queue[ index ].finish.call( this );
}
}
// turn off finishing flag
delete data.finish;
});
}
});
jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = jQuery.fn[ name ];
jQuery.fn[ name ] = function( speed, easing, callback ) {
return speed == null || typeof speed === "boolean" ?
cssFn.apply( this, arguments ) :
this.animate( genFx( name, true ), speed, easing, callback );
};
});
// Generate shortcuts for custom animations
jQuery.each({
slideDown: genFx("show"),
slideUp: genFx("hide"),
slideToggle: genFx("toggle"),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});
jQuery.timers = [];
jQuery.fx.tick = function() {
var timer,
timers = jQuery.timers,
i = 0;
fxNow = jQuery.now();
for ( ; i < timers.length; i++ ) {
timer = timers[ i ];
// Checks the timer has not already been removed
if ( !timer() && timers[ i ] === timer ) {
timers.splice( i--, 1 );
}
}
if ( !timers.length ) {
jQuery.fx.stop();
}
fxNow = undefined;
};
jQuery.fx.timer = function( timer ) {
jQuery.timers.push( timer );
if ( timer() ) {
jQuery.fx.start();
} else {
jQuery.timers.pop();
}
};
jQuery.fx.interval = 13;
jQuery.fx.start = function() {
if ( !timerId ) {
timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
}
};
jQuery.fx.stop = function() {
clearInterval( timerId );
timerId = null;
};
jQuery.fx.speeds = {
slow: 600,
fast: 200,
// Default speed
_default: 400
};
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
jQuery.fn.delay = function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
return this.queue( type, function( next, hooks ) {
var timeout = setTimeout( next, time );
hooks.stop = function() {
clearTimeout( timeout );
};
});
};
(function() {
// Minified: var a,b,c,d,e
var input, div, select, a, opt;
// Setup
div = document.createElement( "div" );
div.setAttribute( "className", "t" );
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
a = div.getElementsByTagName("a")[ 0 ];
// First batch of tests.
select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
input = div.getElementsByTagName("input")[ 0 ];
a.style.cssText = "top:1px";
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
support.getSetAttribute = div.className !== "t";
// Get the style information from getAttribute
// (IE uses .cssText instead)
support.style = /top/.test( a.getAttribute("style") );
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
support.hrefNormalized = a.getAttribute("href") === "/a";
// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
support.checkOn = !!input.value;
// Make sure that a selected-by-default option has a working selected property.
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
support.optSelected = opt.selected;
// Tests for enctype support on a form (#6743)
support.enctype = !!document.createElement("form").enctype;
// Make sure that the options inside disabled selects aren't marked as disabled
// (WebKit marks them as disabled)
select.disabled = true;
support.optDisabled = !opt.disabled;
// Support: IE8 only
// Check if we can trust getAttribute("value")
input = document.createElement( "input" );
input.setAttribute( "value", "" );
support.input = input.getAttribute( "value" ) === "";
// Check if an input maintains its value after becoming a radio
input.value = "t";
input.setAttribute( "type", "radio" );
support.radioValue = input.value === "t";
})();
var rreturn = /\r/g;
jQuery.fn.extend({
val: function( value ) {
var hooks, ret, isFunction,
elem = this[0];
if ( !arguments.length ) {
if ( elem ) {
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret;
}
ret = elem.value;
return typeof ret === "string" ?
// handle most common string cases
ret.replace(rreturn, "") :
// handle cases where value is null/undef or number
ret == null ? "" : ret;
}
return;
}
isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
var val;
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
val = value.call( this, i, jQuery( this ).val() );
} else {
val = value;
}
// Treat null/undefined as ""; convert numbers to string
if ( val == null ) {
val = "";
} else if ( typeof val === "number" ) {
val += "";
} else if ( jQuery.isArray( val ) ) {
val = jQuery.map( val, function( value ) {
return value == null ? "" : value + "";
});
}
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
// If set returns undefined, fall back to normal setting
if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
this.value = val;
}
});
}
});
jQuery.extend({
valHooks: {
option: {
get: function( elem ) {
var val = jQuery.find.attr( elem, "value" );
return val != null ?
val :
// Support: IE10-11+
// option.text throws exceptions (#14686, #14858)
jQuery.trim( jQuery.text( elem ) );
}
},
select: {
get: function( elem ) {
var value, option,
options = elem.options,
index = elem.selectedIndex,
one = elem.type === "select-one" || index < 0,
values = one ? null : [],
max = one ? index + 1 : options.length,
i = index < 0 ?
max :
one ? index : 0;
// Loop through all the selected options
for ( ; i < max; i++ ) {
option = options[ i ];
// oldIE doesn't update selected after form reset (#2551)
if ( ( option.selected || i === index ) &&
// Don't return options that are disabled or in a disabled optgroup
( support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
// Get the specific value for the option
value = jQuery( option ).val();
// We don't need an array for one selects
if ( one ) {
return value;
}
// Multi-Selects return an array
values.push( value );
}
}
return values;
},
set: function( elem, value ) {
var optionSet, option,
options = elem.options,
values = jQuery.makeArray( value ),
i = options.length;
while ( i-- ) {
option = options[ i ];
if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) >= 0 ) {
// Support: IE6
// When new option element is added to select box we need to
// force reflow of newly added node in order to workaround delay
// of initialization properties
try {
option.selected = optionSet = true;
} catch ( _ ) {
// Will be executed only in IE6
option.scrollHeight;
}
} else {
option.selected = false;
}
}
// Force browsers to behave consistently when non-matching value is set
if ( !optionSet ) {
elem.selectedIndex = -1;
}
return options;
}
}
}
});
// Radios and checkboxes getter/setter
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
}
}
};
if ( !support.checkOn ) {
jQuery.valHooks[ this ].get = function( elem ) {
// Support: Webkit
// "" is returned instead of "on" if a value isn't specified
return elem.getAttribute("value") === null ? "on" : elem.value;
};
}
});
var nodeHook, boolHook,
attrHandle = jQuery.expr.attrHandle,
ruseDefault = /^(?:checked|selected)$/i,
getSetAttribute = support.getSetAttribute,
getSetInput = support.input;
jQuery.fn.extend({
attr: function( name, value ) {
return access( this, jQuery.attr, name, value, arguments.length > 1 );
},
removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name );
});
}
});
jQuery.extend({
attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === strundefined ) {
return jQuery.prop( elem, name, value );
}
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] ||
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
},
removeAttr: function( elem, value ) {
var name, propName,
i = 0,
attrNames = value && value.match( rnotwhite );
if ( attrNames && elem.nodeType === 1 ) {
while ( (name = attrNames[i++]) ) {
propName = jQuery.propFix[ name ] || name;
// Boolean attributes get special treatment (#10870)
if ( jQuery.expr.match.bool.test( name ) ) {
// Set corresponding property to false
if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
elem[ propName ] = false;
// Support: IE<9
// Also clear defaultChecked/defaultSelected (if appropriate)
} else {
elem[ jQuery.camelCase( "default-" + name ) ] =
elem[ propName ] = false;
}
// See #9699 for explanation of this approach (setting first, then removal)
} else {
jQuery.attr( elem, name, "" );
}
elem.removeAttribute( getSetAttribute ? name : propName );
}
}
},
attrHooks: {
type: {
set: function( elem, value ) {
if ( !support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
// Setting the type on a radio button after the value resets the value in IE6-9
// Reset value to default in case type is set after value during creation
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
elem.value = val;
}
return value;
}
}
}
}
});
// Hook for boolean attributes
boolHook = {
set: function( elem, value, name ) {
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
// IE<8 needs the *property* name
elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
// Use defaultChecked and defaultSelected for oldIE
} else {
elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
}
return name;
}
};
// Retrieve booleans specially
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
var getter = attrHandle[ name ] || jQuery.find.attr;
attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
function( elem, name, isXML ) {
var ret, handle;
if ( !isXML ) {
// Avoid an infinite loop by temporarily removing this function from the getter
handle = attrHandle[ name ];
attrHandle[ name ] = ret;
ret = getter( elem, name, isXML ) != null ?
name.toLowerCase() :
null;
attrHandle[ name ] = handle;
}
return ret;
} :
function( elem, name, isXML ) {
if ( !isXML ) {
return elem[ jQuery.camelCase( "default-" + name ) ] ?
name.toLowerCase() :
null;
}
};
});
// fix oldIE attroperties
if ( !getSetInput || !getSetAttribute ) {
jQuery.attrHooks.value = {
set: function( elem, value, name ) {
if ( jQuery.nodeName( elem, "input" ) ) {
// Does not return so that setAttribute is also used
elem.defaultValue = value;
} else {
// Use nodeHook if defined (#1954); otherwise setAttribute is fine
return nodeHook && nodeHook.set( elem, value, name );
}
}
};
}
// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !getSetAttribute ) {
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = {
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
elem.setAttributeNode(
(ret = elem.ownerDocument.createAttribute( name ))
);
}
ret.value = value += "";
// Break association with cloned elements by also using setAttribute (#9646)
if ( name === "value" || value === elem.getAttribute( name ) ) {
return value;
}
}
};
// Some attributes are constructed with empty-string values when not defined
attrHandle.id = attrHandle.name = attrHandle.coords =
function( elem, name, isXML ) {
var ret;
if ( !isXML ) {
return (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
ret.value :
null;
}
};
// Fixing value retrieval on a button requires this module
jQuery.valHooks.button = {
get: function( elem, name ) {
var ret = elem.getAttributeNode( name );
if ( ret && ret.specified ) {
return ret.value;
}
},
set: nodeHook.set
};
// Set contenteditable to false on removals(#10429)
// Setting to empty string throws an error as an invalid value
jQuery.attrHooks.contenteditable = {
set: function( elem, value, name ) {
nodeHook.set( elem, value === "" ? false : value, name );
}
};
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
};
});
}
if ( !support.style ) {
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
// Note: IE uppercases css property names, but if we were to .toLowerCase()
// .cssText, that would destroy case senstitivity in URL's, like in "background"
return elem.style.cssText || undefined;
},
set: function( elem, value ) {
return ( elem.style.cssText = value + "" );
}
};
}
var rfocusable = /^(?:input|select|textarea|button|object)$/i,
rclickable = /^(?:a|area)$/i;
jQuery.fn.extend({
prop: function( name, value ) {
return access( this, jQuery.prop, name, value, arguments.length > 1 );
},
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
});
}
});
jQuery.extend({
propFix: {
"for": "htmlFor",
"class": "className"
},
prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
}
if ( value !== undefined ) {
return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
ret :
( elem[ name ] = value );
} else {
return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
ret :
elem[ name ];
}
},
propHooks: {
tabIndex: {
get: function( elem ) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
// Use proper attribute retrieval(#12072)
var tabindex = jQuery.find.attr( elem, "tabindex" );
return tabindex ?
parseInt( tabindex, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
-1;
}
}
}
});
// Some attributes require a special call on IE
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !support.hrefNormalized ) {
// href/src property should get the full normalized URL (#10299/#12915)
jQuery.each([ "href", "src" ], function( i, name ) {
jQuery.propHooks[ name ] = {
get: function( elem ) {
return elem.getAttribute( name, 4 );
}
};
});
}
// Support: Safari, IE9+
// mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !support.optSelected ) {
jQuery.propHooks.selected = {
get: function( elem ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
return null;
}
};
}
jQuery.each([
"tabIndex",
"readOnly",
"maxLength",
"cellSpacing",
"cellPadding",
"rowSpan",
"colSpan",
"useMap",
"frameBorder",
"contentEditable"
], function() {
jQuery.propFix[ this.toLowerCase() ] = this;
});
// IE6/7 call enctype encoding
if ( !support.enctype ) {
jQuery.propFix.enctype = "encoding";
}
var rclass = /[\t\r\n\f]/g;
jQuery.fn.extend({
addClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
i = 0,
len = this.length,
proceed = typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
if ( proceed ) {
// The disjunction here is for better compressibility (see removeClass)
classes = ( value || "" ).match( rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
}
// only assign if different to avoid unneeded rendering.
finalValue = jQuery.trim( cur );
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
}
}
}
return this;
},
removeClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
i = 0,
len = this.length,
proceed = arguments.length === 0 || typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).removeClass( value.call( this, j, this.className ) );
});
}
if ( proceed ) {
classes = ( value || "" ).match( rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
""
);
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
cur = cur.replace( " " + clazz + " ", " " );
}
}
// only assign if different to avoid unneeded rendering.
finalValue = value ? jQuery.trim( cur ) : "";
if ( elem.className !== finalValue ) {
elem.className = finalValue;
}
}
}
}
return this;
},
toggleClass: function( value, stateVal ) {
var type = typeof value;
if ( typeof stateVal === "boolean" && type === "string" ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
}
if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) {
jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
});
}
return this.each(function() {
if ( type === "string" ) {
// toggle individual class names
var className,
i = 0,
self = jQuery( this ),
classNames = value.match( rnotwhite ) || [];
while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
}
}
// Toggle whole class name
} else if ( type === strundefined || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery._data( this, "__className__", this.className );
}
// If the element has a class name or if we're passed "false",
// then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored.
this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}
});
},
hasClass: function( selector ) {
var className = " " + selector + " ",
i = 0,
l = this.length;
for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
return true;
}
}
return false;
}
});
// Return jQuery for attributes-only inclusion
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
});
jQuery.fn.extend({
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
},
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
}
});
var nonce = jQuery.now();
var rquery = (/\?/);
var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
jQuery.parseJSON = function( data ) {
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
// Support: Android 2.3
// Workaround failure to string-cast null input
return window.JSON.parse( data + "" );
}
var requireNonComma,
depth = null,
str = jQuery.trim( data + "" );
// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
// after removing valid tokens
return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
// Force termination if we see a misplaced comma
if ( requireNonComma && comma ) {
depth = 0;
}
// Perform no more replacements after returning to outermost depth
if ( depth === 0 ) {
return token;
}
// Commas must not follow "[", "{", or ","
requireNonComma = open || comma;
// Determine new depth
// array/object open ("[" or "{"): depth += true - false (increment)
// array/object close ("]" or "}"): depth += false - true (decrement)
// other cases ("," or primitive): depth += true - true (numeric cast)
depth += !close - !open;
// Remove this token
return "";
}) ) ?
( Function( "return " + str ) )() :
jQuery.error( "Invalid JSON: " + data );
};
// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
}
try {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
xml = tmp.parseFromString( data, "text/xml" );
} else { // IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
} catch( e ) {
xml = undefined;
}
if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
};
var
// Document location
ajaxLocParts,
ajaxLocation,
rhash = /#.*$/,
rts = /([?&])_=[^&]*/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
// #7653, #8125, #8152: local protocol detection
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
rnoContent = /^(?:GET|HEAD)$/,
rprotocol = /^\/\//,
rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
/* Prefilters
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
* 2) These are called:
* - BEFORE asking for a transport
* - AFTER param serialization (s.data is a string if s.processData is true)
* 3) key is the dataType
* 4) the catchall symbol "*" can be used
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
*/
prefilters = {},
/* Transports bindings
* 1) key is the dataType
* 2) the catchall symbol "*" can be used
* 3) selection will start with transport dataType and THEN go to "*" if needed
*/
transports = {},
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
allTypes = "*/".concat("*");
// #8138, IE may throw an exception when accessing
// a field from window.location if document.domain has been set
try {
ajaxLocation = location.href;
} catch( e ) {
// Use the href attribute of an A element
// since IE will modify it given document.location
ajaxLocation = document.createElement( "a" );
ajaxLocation.href = "";
ajaxLocation = ajaxLocation.href;
}
// Segment location into parts
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) {
// dataTypeExpression is optional and defaults to "*"
return function( dataTypeExpression, func ) {
if ( typeof dataTypeExpression !== "string" ) {
func = dataTypeExpression;
dataTypeExpression = "*";
}
var dataType,
i = 0,
dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
if ( jQuery.isFunction( func ) ) {
// For each dataType in the dataTypeExpression
while ( (dataType = dataTypes[i++]) ) {
// Prepend if requested
if ( dataType.charAt( 0 ) === "+" ) {
dataType = dataType.slice( 1 ) || "*";
(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
// Otherwise append
} else {
(structure[ dataType ] = structure[ dataType ] || []).push( func );
}
}
}
};
}
// Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
var inspected = {},
seekingTransport = ( structure === transports );
function inspect( dataType ) {
var selected;
inspected[ dataType ] = true;
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
options.dataTypes.unshift( dataTypeOrTransport );
inspect( dataTypeOrTransport );
return false;
} else if ( seekingTransport ) {
return !( selected = dataTypeOrTransport );
}
});
return selected;
}
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
}
// A special extend for ajax options
// that takes "flat" options (not to be deep extended)
// Fixes #9887
function ajaxExtend( target, src ) {
var deep, key,
flatOptions = jQuery.ajaxSettings.flatOptions || {};
for ( key in src ) {
if ( src[ key ] !== undefined ) {
( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
}
}
if ( deep ) {
jQuery.extend( true, target, deep );
}
return target;
}
/* Handles responses to an ajax request:
* - finds the right dataType (mediates between content-type and expected dataType)
* - returns the corresponding response
*/
function ajaxHandleResponses( s, jqXHR, responses ) {
var firstDataType, ct, finalDataType, type,
contents = s.contents,
dataTypes = s.dataTypes;
// Remove auto dataType and get content-type in the process
while ( dataTypes[ 0 ] === "*" ) {
dataTypes.shift();
if ( ct === undefined ) {
ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
}
}
// Check if we're dealing with a known content-type
if ( ct ) {
for ( type in contents ) {
if ( contents[ type ] && contents[ type ].test( ct ) ) {
dataTypes.unshift( type );
break;
}
}
}
// Check to see if we have a response for the expected dataType
if ( dataTypes[ 0 ] in responses ) {
finalDataType = dataTypes[ 0 ];
} else {
// Try convertible dataTypes
for ( type in responses ) {
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
finalDataType = type;
break;
}
if ( !firstDataType ) {
firstDataType = type;
}
}
// Or just use first one
finalDataType = finalDataType || firstDataType;
}
// If we found a dataType
// We add the dataType to the list if needed
// and return the corresponding response
if ( finalDataType ) {
if ( finalDataType !== dataTypes[ 0 ] ) {
dataTypes.unshift( finalDataType );
}
return responses[ finalDataType ];
}
}
/* Chain conversions given the request and the original response
* Also sets the responseXXX fields on the jqXHR instance
*/
function ajaxConvert( s, response, jqXHR, isSuccess ) {
var conv2, current, conv, tmp, prev,
converters = {},
// Work with a copy of dataTypes in case we need to modify it for conversion
dataTypes = s.dataTypes.slice();
// Create converters map with lowercased keys
if ( dataTypes[ 1 ] ) {
for ( conv in s.converters ) {
converters[ conv.toLowerCase() ] = s.converters[ conv ];
}
}
current = dataTypes.shift();
// Convert to each sequential dataType
while ( current ) {
if ( s.responseFields[ current ] ) {
jqXHR[ s.responseFields[ current ] ] = response;
}
// Apply the dataFilter if provided
if ( !prev && isSuccess && s.dataFilter ) {
response = s.dataFilter( response, s.dataType );
}
prev = current;
current = dataTypes.shift();
if ( current ) {
// There's only work to do if current dataType is non-auto
if ( current === "*" ) {
current = prev;
// Convert response if prev dataType is non-auto and differs from current
} else if ( prev !== "*" && prev !== current ) {
// Seek a direct converter
conv = converters[ prev + " " + current ] || converters[ "* " + current ];
// If none found, seek a pair
if ( !conv ) {
for ( conv2 in converters ) {
// If conv2 outputs current
tmp = conv2.split( " " );
if ( tmp[ 1 ] === current ) {
// If prev can be converted to accepted input
conv = converters[ prev + " " + tmp[ 0 ] ] ||
converters[ "* " + tmp[ 0 ] ];
if ( conv ) {
// Condense equivalence converters
if ( conv === true ) {
conv = converters[ conv2 ];
// Otherwise, insert the intermediate dataType
} else if ( converters[ conv2 ] !== true ) {
current = tmp[ 0 ];
dataTypes.unshift( tmp[ 1 ] );
}
break;
}
}
}
}
// Apply converter (if not an equivalence)
if ( conv !== true ) {
// Unless errors are allowed to bubble, catch and return them
if ( conv && s[ "throws" ] ) {
response = conv( response );
} else {
try {
response = conv( response );
} catch ( e ) {
return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
}
}
}
}
}
}
return { state: "success", data: response };
}
jQuery.extend({
// Counter for holding the number of active queries
active: 0,
// Last-Modified header cache for next request
lastModified: {},
etag: {},
ajaxSettings: {
url: ajaxLocation,
type: "GET",
isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
global: true,
processData: true,
async: true,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
/*
timeout: 0,
data: null,
dataType: null,
username: null,
password: null,
cache: null,
throws: false,
traditional: false,
headers: {},
*/
accepts: {
"*": allTypes,
text: "text/plain",
html: "text/html",
xml: "application/xml, text/xml",
json: "application/json, text/javascript"
},
contents: {
xml: /xml/,
html: /html/,
json: /json/
},
responseFields: {
xml: "responseXML",
text: "responseText",
json: "responseJSON"
},
// Data converters
// Keys separate source (or catchall "*") and destination types with a single space
converters: {
// Convert anything to text
"* text": String,
// Text to html (true = no transformation)
"text html": true,
// Evaluate text as a json expression
"text json": jQuery.parseJSON,
// Parse text as xml
"text xml": jQuery.parseXML
},
// For options that shouldn't be deep extended:
// you can add your own custom options here if
// and when you create one that shouldn't be
// deep extended (see ajaxExtend)
flatOptions: {
url: true,
context: true
}
},
// Creates a full fledged settings object into target
// with both ajaxSettings and settings fields.
// If target is omitted, writes into ajaxSettings.
ajaxSetup: function( target, settings ) {
return settings ?
// Building a settings object
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
// Extending ajaxSettings
ajaxExtend( jQuery.ajaxSettings, target );
},
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
ajaxTransport: addToPrefiltersOrTransports( transports ),
// Main method
ajax: function( url, options ) {
// If url is an object, simulate pre-1.5 signature
if ( typeof url === "object" ) {
options = url;
url = undefined;
}
// Force options to be an object
options = options || {};
var // Cross-domain detection vars
parts,
// Loop variable
i,
// URL without anti-cache param
cacheURL,
// Response headers as string
responseHeadersString,
// timeout handle
timeoutTimer,
// To know if global events are to be dispatched
fireGlobals,
transport,
// Response headers
responseHeaders,
// Create the final options object
s = jQuery.ajaxSetup( {}, options ),
// Callbacks context
callbackContext = s.context || s,
// Context for global events is callbackContext if it is a DOM node or jQuery collection
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
jQuery( callbackContext ) :
jQuery.event,
// Deferreds
deferred = jQuery.Deferred(),
completeDeferred = jQuery.Callbacks("once memory"),
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
// Fake xhr
jqXHR = {
readyState: 0,
// Builds headers hashtable if needed
getResponseHeader: function( key ) {
var match;
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
while ( (match = rheaders.exec( responseHeadersString )) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
match = responseHeaders[ key.toLowerCase() ];
}
return match == null ? null : match;
},
// Raw string
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
},
// Caches the header
setRequestHeader: function( name, value ) {
var lname = name.toLowerCase();
if ( !state ) {
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
requestHeaders[ name ] = value;
}
return this;
},
// Overrides response content-type header
overrideMimeType: function( type ) {
if ( !state ) {
s.mimeType = type;
}
return this;
},
// Status-dependent callbacks
statusCode: function( map ) {
var code;
if ( map ) {
if ( state < 2 ) {
for ( code in map ) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
}
} else {
// Execute the appropriate callbacks
jqXHR.always( map[ jqXHR.status ] );
}
}
return this;
},
// Cancel the request
abort: function( statusText ) {
var finalText = statusText || strAbort;
if ( transport ) {
transport.abort( finalText );
}
done( 0, finalText );
return this;
}
};
// Attach deferreds
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
// Remove hash character (#7531: and string promotion)
// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
// Handle falsy url in the settings object (#10093: consistency with old signature)
// We also use the url parameter if available
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
// Alias method option to type as per ticket #12004
s.type = options.method || options.type || s.method || s.type;
// Extract dataTypes list
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
// A cross-domain request is in order when we have a protocol:host:port mismatch
if ( s.crossDomain == null ) {
parts = rurl.exec( s.url.toLowerCase() );
s.crossDomain = !!( parts &&
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
);
}
// Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
// Apply prefilters
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
// If request was aborted inside a prefilter, stop there
if ( state === 2 ) {
return jqXHR;
}
// We can fire global events as of now if asked to
fireGlobals = s.global;
// Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger("ajaxStart");
}
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
s.hasContent = !rnoContent.test( s.type );
// Save the URL in case we're toying with the If-Modified-Since
// and/or If-None-Match header later on
cacheURL = s.url;
// More options handling for requests with no content
if ( !s.hasContent ) {
// If data is available, append data to url
if ( s.data ) {
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Add anti-cache in url if needed
if ( s.cache === false ) {
s.url = rts.test( cacheURL ) ?
// If there is already a '_' parameter, set its value
cacheURL.replace( rts, "$1_=" + nonce++ ) :
// Otherwise add one to the end
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
}
if ( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
}
}
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
jqXHR.setRequestHeader( "Content-Type", s.contentType );
}
// Set the Accepts header for the server, depending on the dataType
jqXHR.setRequestHeader(
"Accept",
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
s.accepts[ "*" ]
);
// Check for headers option
for ( i in s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
}
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
// Abort if not done already and return
return jqXHR.abort();
}
// aborting is no longer a cancellation
strAbort = "abort";
// Install callbacks on deferreds
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
}
// Get transport
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
// If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
jqXHR.readyState = 1;
// Send global event
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
// Timeout
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout );
}
try {
state = 1;
transport.send( requestHeaders, done );
} catch ( e ) {
// Propagate exception as error if not done
if ( state < 2 ) {
done( -1, e );
// Simply rethrow otherwise
} else {
throw e;
}
}
}
// Callback for when everything is done
function done( status, nativeStatusText, responses, headers ) {
var isSuccess, success, error, response, modified,
statusText = nativeStatusText;
// Called once
if ( state === 2 ) {
return;
}
// State is "done" now
state = 2;
// Clear timeout if it exists
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
// Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined;
// Cache response headers
responseHeadersString = headers || "";
// Set readyState
jqXHR.readyState = status > 0 ? 4 : 0;
// Determine if successful
isSuccess = status >= 200 && status < 300 || status === 304;
// Get response data
if ( responses ) {
response = ajaxHandleResponses( s, jqXHR, responses );
}
// Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert( s, response, jqXHR, isSuccess );
// If successful, handle type chaining
if ( isSuccess ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
modified = jqXHR.getResponseHeader("Last-Modified");
if ( modified ) {
jQuery.lastModified[ cacheURL ] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if ( modified ) {
jQuery.etag[ cacheURL ] = modified;
}
}
// if no content
if ( status === 204 || s.type === "HEAD" ) {
statusText = "nocontent";
// if not modified
} else if ( status === 304 ) {
statusText = "notmodified";
// If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
if ( status || !statusText ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
}
}
}
// Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
// Success/Error
if ( isSuccess ) {
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
}
// Status-dependent callbacks
jqXHR.statusCode( statusCode );
statusCode = undefined;
if ( fireGlobals ) {
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
[ jqXHR, s, isSuccess ? success : error ] );
}
// Complete
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
// Handle the global AJAX counter
if ( !( --jQuery.active ) ) {
jQuery.event.trigger("ajaxStop");
}
}
}
return jqXHR;
},
getJSON: function( url, data, callback ) {
return jQuery.get( url, data, callback, "json" );
},
getScript: function( url, callback ) {
return jQuery.get( url, undefined, callback, "script" );
}
});
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) {
// shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
}
return jQuery.ajax({
url: url,
type: method,
dataType: type,
data: data,
success: callback
});
};
});
// Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
jQuery.fn[ type ] = function( fn ) {
return this.on( type, fn );
};
});
jQuery._evalUrl = function( url ) {
return jQuery.ajax({
url: url,
type: "GET",
dataType: "script",
async: false,
global: false,
"throws": true
});
};
jQuery.fn.extend({
wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapAll( html.call(this, i) );
});
}
if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
if ( this[0].parentNode ) {
wrap.insertBefore( this[0] );
}
wrap.map(function() {
var elem = this;
while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}
return elem;
}).append( this );
}
return this;
},
wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapInner( html.call(this, i) );
});
}
return this.each(function() {
var self = jQuery( this ),
contents = self.contents();
if ( contents.length ) {
contents.wrapAll( html );
} else {
self.append( html );
}
});
},
wrap: function( html ) {
var isFunction = jQuery.isFunction( html );
return this.each(function(i) {
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
});
},
unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
}
});
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
(!support.reliableHiddenOffsets() &&
((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
var r20 = /%20/g,
rbracket = /\[\]$/,
rCRLF = /\r?\n/g,
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
rsubmittable = /^(?:input|select|textarea|keygen)/i;
function buildParams( prefix, obj, traditional, add ) {
var name;
if ( jQuery.isArray( obj ) ) {
// Serialize array item.
jQuery.each( obj, function( i, v ) {
if ( traditional || rbracket.test( prefix ) ) {
// Treat each array item as a scalar.
add( prefix, v );
} else {
// Item is non-scalar (array or object), encode its numeric index.
buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
}
});
} else if ( !traditional && jQuery.type( obj ) === "object" ) {
// Serialize object item.
for ( name in obj ) {
buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
}
} else {
// Serialize scalar item.
add( prefix, obj );
}
}
// Serialize an array of form elements or a set of
// key/values into a query string
jQuery.param = function( a, traditional ) {
var prefix,
s = [],
add = function( key, value ) {
// If value is a function, invoke it and return its value
value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
};
// Set traditional to true for jQuery <= 1.3.2 behavior.
if ( traditional === undefined ) {
traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
}
// If an array was passed in, assume that it is an array of form elements.
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
// Serialize the form elements
jQuery.each( a, function() {
add( this.name, this.value );
});
} else {
// If traditional, encode the "old" way (the way 1.3.2 or older
// did it), otherwise encode params recursively.
for ( prefix in a ) {
buildParams( prefix, a[ prefix ], traditional, add );
}
}
// Return the resulting serialization
return s.join( "&" ).replace( r20, "+" );
};
jQuery.fn.extend({
serialize: function() {
return jQuery.param( this.serializeArray() );
},
serializeArray: function() {
return this.map(function() {
// Can add propHook for "elements" to filter or add form elements
var elements = jQuery.prop( this, "elements" );
return elements ? jQuery.makeArray( elements ) : this;
})
.filter(function() {
var type = this.type;
// Use .is(":disabled") so that fieldset[disabled] works
return this.name && !jQuery( this ).is( ":disabled" ) &&
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
( this.checked || !rcheckableType.test( type ) );
})
.map(function( i, elem ) {
var val = jQuery( this ).val();
return val == null ?
null :
jQuery.isArray( val ) ?
jQuery.map( val, function( val ) {
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}) :
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}).get();
}
});
// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
// Support: IE6+
function() {
// XHR cannot access local files, always use ActiveX for that case
return !this.isLocal &&
// Support: IE7-8
// oldIE XHR does not support non-RFC2616 methods (#13240)
// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
// Although this check for six methods instead of eight
// since IE also does not support "trace" and "connect"
/^(get|post|head|put|delete|options)$/i.test( this.type ) &&
createStandardXHR() || createActiveXHR();
} :
// For all other browsers, use the standard XMLHttpRequest object
createStandardXHR;
var xhrId = 0,
xhrCallbacks = {},
xhrSupported = jQuery.ajaxSettings.xhr();
// Support: IE<10
// Open requests must be manually aborted on unload (#5280)
if ( window.ActiveXObject ) {
jQuery( window ).on( "unload", function() {
for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ]( undefined, true );
}
});
}
// Determine support properties
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
xhrSupported = support.ajax = !!xhrSupported;
// Create transport if the browser can provide an xhr
if ( xhrSupported ) {
jQuery.ajaxTransport(function( options ) {
// Cross domain only allowed if supported through XMLHttpRequest
if ( !options.crossDomain || support.cors ) {
var callback;
return {
send: function( headers, complete ) {
var i,
xhr = options.xhr(),
id = ++xhrId;
// Open the socket
xhr.open( options.type, options.url, options.async, options.username, options.password );
// Apply custom fields if provided
if ( options.xhrFields ) {
for ( i in options.xhrFields ) {
xhr[ i ] = options.xhrFields[ i ];
}
}
// Override mime type if needed
if ( options.mimeType && xhr.overrideMimeType ) {
xhr.overrideMimeType( options.mimeType );
}
// X-Requested-With header
// For cross-domain requests, seeing as conditions for a preflight are
// akin to a jigsaw puzzle, we simply never set it to be sure.
// (it can always be set on a per-request basis or even using ajaxSetup)
// For same-domain requests, won't change header if already provided.
if ( !options.crossDomain && !headers["X-Requested-With"] ) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
// Set headers
for ( i in headers ) {
// Support: IE<9
// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
// request header to a null-value.
//
// To keep consistent with other XHR implementations, cast the value
// to string and ignore `undefined`.
if ( headers[ i ] !== undefined ) {
xhr.setRequestHeader( i, headers[ i ] + "" );
}
}
// Do send the request
// This may raise an exception which is actually
// handled in jQuery.ajax (so no try/catch here)
xhr.send( ( options.hasContent && options.data ) || null );
// Listener
callback = function( _, isAbort ) {
var status, statusText, responses;
// Was never called and is aborted or complete
if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
// Clean up
delete xhrCallbacks[ id ];
callback = undefined;
xhr.onreadystatechange = jQuery.noop;
// Abort manually if needed
if ( isAbort ) {
if ( xhr.readyState !== 4 ) {
xhr.abort();
}
} else {
responses = {};
status = xhr.status;
// Support: IE<10
// Accessing binary-data responseText throws an exception
// (#11426)
if ( typeof xhr.responseText === "string" ) {
responses.text = xhr.responseText;
}
// Firefox throws an exception when accessing
// statusText for faulty cross-domain requests
try {
statusText = xhr.statusText;
} catch( e ) {
// We normalize with Webkit giving an empty statusText
statusText = "";
}
// Filter status for non standard behaviors
// If the request is local and we have data: assume a success
// (success with no data won't get notified, that's the best we
// can do given current implementations)
if ( !status && options.isLocal && !options.crossDomain ) {
status = responses.text ? 200 : 404;
// IE - #1450: sometimes returns 1223 when it should be 204
} else if ( status === 1223 ) {
status = 204;
}
}
}
// Call complete if needed
if ( responses ) {
complete( status, statusText, responses, xhr.getAllResponseHeaders() );
}
};
if ( !options.async ) {
// if we're in sync mode we fire the callback
callback();
} else if ( xhr.readyState === 4 ) {
// (IE6 & IE7) if it's in cache and has been
// retrieved directly we need to fire the callback
setTimeout( callback );
} else {
// Add to the list of active xhr callbacks
xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
}
},
abort: function() {
if ( callback ) {
callback( undefined, true );
}
}
};
}
});
}
// Functions to create xhrs
function createStandardXHR() {
try {
return new window.XMLHttpRequest();
} catch( e ) {}
}
function createActiveXHR() {
try {
return new window.ActiveXObject( "Microsoft.XMLHTTP" );
} catch( e ) {}
}
// Install script dataType
jQuery.ajaxSetup({
accepts: {
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
},
contents: {
script: /(?:java|ecma)script/
},
converters: {
"text script": function( text ) {
jQuery.globalEval( text );
return text;
}
}
});
// Handle cache's special case and global
jQuery.ajaxPrefilter( "script", function( s ) {
if ( s.cache === undefined ) {
s.cache = false;
}
if ( s.crossDomain ) {
s.type = "GET";
s.global = false;
}
});
// Bind script tag hack transport
jQuery.ajaxTransport( "script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.head || jQuery("head")[0] || document.documentElement;
return {
send: function( _, callback ) {
script = document.createElement("script");
script.async = true;
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {
if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if ( script.parentNode ) {
script.parentNode.removeChild( script );
}
// Dereference the script
script = null;
// Callback if not abort
if ( !isAbort ) {
callback( 200, "success" );
}
}
};
// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
// Use native DOM manipulation to avoid our domManip AJAX trickery
head.insertBefore( script, head.firstChild );
},
abort: function() {
if ( script ) {
script.onload( undefined, true );
}
}
};
}
});
var oldCallbacks = [],
rjsonp = /(=)\?(?=&|$)|\?\?/;
// Default jsonp settings
jQuery.ajaxSetup({
jsonp: "callback",
jsonpCallback: function() {
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
this[ callback ] = true;
return callback;
}
});
// Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
var callbackName, overwritten, responseContainer,
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
"url" :
typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
);
// Handle iff the expected data type is "jsonp" or we have a parameter to set
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
// Get callback name, remembering preexisting value associated with it
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
s.jsonpCallback() :
s.jsonpCallback;
// Insert callback into url or form data
if ( jsonProp ) {
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
} else if ( s.jsonp !== false ) {
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
}
// Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( !responseContainer ) {
jQuery.error( callbackName + " was not called" );
}
return responseContainer[ 0 ];
};
// force json dataType
s.dataTypes[ 0 ] = "json";
// Install callback
overwritten = window[ callbackName ];
window[ callbackName ] = function() {
responseContainer = arguments;
};
// Clean-up function (fires after converters)
jqXHR.always(function() {
// Restore preexisting value
window[ callbackName ] = overwritten;
// Save back as free
if ( s[ callbackName ] ) {
// make sure that re-using the options doesn't screw things around
s.jsonpCallback = originalSettings.jsonpCallback;
// save the callback name for future use
oldCallbacks.push( callbackName );
}
// Call if it was a function and we have a response
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
overwritten( responseContainer[ 0 ] );
}
responseContainer = overwritten = undefined;
});
// Delegate to script
return "script";
}
});
// data: string of html
// context (optional): If specified, the fragment will be created in this context, defaults to document
// keepScripts (optional): If true, will include scripts passed in the html string
jQuery.parseHTML = function( data, context, keepScripts ) {
if ( !data || typeof data !== "string" ) {
return null;
}
if ( typeof context === "boolean" ) {
keepScripts = context;
context = false;
}
context = context || document;
var parsed = rsingleTag.exec( data ),
scripts = !keepScripts && [];
// Single tag
if ( parsed ) {
return [ context.createElement( parsed[1] ) ];
}
parsed = jQuery.buildFragment( [ data ], context, scripts );
if ( scripts && scripts.length ) {
jQuery( scripts ).remove();
}
return jQuery.merge( [], parsed.childNodes );
};
// Keep a copy of the old load method
var _load = jQuery.fn.load;
/**
* Load a url into a page
*/
jQuery.fn.load = function( url, params, callback ) {
if ( typeof url !== "string" && _load ) {
return _load.apply( this, arguments );
}
var selector, response, type,
self = this,
off = url.indexOf(" ");
if ( off >= 0 ) {
selector = jQuery.trim( url.slice( off, url.length ) );
url = url.slice( 0, off );
}
// If it's a function
if ( jQuery.isFunction( params ) ) {
// We assume that it's the callback
callback = params;
params = undefined;
// Otherwise, build a param string
} else if ( params && typeof params === "object" ) {
type = "POST";
}
// If we have elements to modify, make the request
if ( self.length > 0 ) {
jQuery.ajax({
url: url,
// if "type" variable is undefined, then "GET" method will be used
type: type,
dataType: "html",
data: params
}).done(function( responseText ) {
// Save response for use in complete callback
response = arguments;
self.html( selector ?
// If a selector was specified, locate the right elements in a dummy div
// Exclude scripts to avoid IE 'Permission Denied' errors
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
// Otherwise use the full result
responseText );
}).complete( callback && function( jqXHR, status ) {
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
});
}
return this;
};
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
var docElem = window.document.documentElement;
/**
* Gets a window from an element
*/
function getWindow( elem ) {
return jQuery.isWindow( elem ) ?
elem :
elem.nodeType === 9 ?
elem.defaultView || elem.parentWindow :
false;
}
jQuery.offset = {
setOffset: function( elem, options, i ) {
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
position = jQuery.css( elem, "position" ),
curElem = jQuery( elem ),
props = {};
// set position first, in-case top/left are set even on static elem
if ( position === "static" ) {
elem.style.position = "relative";
}
curOffset = curElem.offset();
curCSSTop = jQuery.css( elem, "top" );
curCSSLeft = jQuery.css( elem, "left" );
calculatePosition = ( position === "absolute" || position === "fixed" ) &&
jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;
// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
if ( calculatePosition ) {
curPosition = curElem.position();
curTop = curPosition.top;
curLeft = curPosition.left;
} else {
curTop = parseFloat( curCSSTop ) || 0;
curLeft = parseFloat( curCSSLeft ) || 0;
}
if ( jQuery.isFunction( options ) ) {
options = options.call( elem, i, curOffset );
}
if ( options.top != null ) {
props.top = ( options.top - curOffset.top ) + curTop;
}
if ( options.left != null ) {
props.left = ( options.left - curOffset.left ) + curLeft;
}
if ( "using" in options ) {
options.using.call( elem, props );
} else {
curElem.css( props );
}
}
};
jQuery.fn.extend({
offset: function( options ) {
if ( arguments.length ) {
return options === undefined ?
this :
this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
}
var docElem, win,
box = { top: 0, left: 0 },
elem = this[ 0 ],
doc = elem && elem.ownerDocument;
if ( !doc ) {
return;
}
docElem = doc.documentElement;
// Make sure it's not a disconnected DOM node
if ( !jQuery.contains( docElem, elem ) ) {
return box;
}
// If we don't have gBCR, just use 0,0 rather than error
// BlackBerry 5, iOS 3 (original iPhone)
if ( typeof elem.getBoundingClientRect !== strundefined ) {
box = elem.getBoundingClientRect();
}
win = getWindow( doc );
return {
top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
};
},
position: function() {
if ( !this[ 0 ] ) {
return;
}
var offsetParent, offset,
parentOffset = { top: 0, left: 0 },
elem = this[ 0 ];
// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// we assume that getBoundingClientRect is available when computed position is fixed
offset = elem.getBoundingClientRect();
} else {
// Get *real* offsetParent
offsetParent = this.offsetParent();
// Get correct offsets
offset = this.offset();
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
}
// Add offsetParent borders
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
}
// Subtract parent offsets and element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
};
},
offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent || docElem;
while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docElem;
});
}
});
// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
var top = /Y/.test( prop );
jQuery.fn[ method ] = function( val ) {
return access( this, function( elem, method, val ) {
var win = getWindow( elem );
if ( val === undefined ) {
return win ? (prop in win) ? win[ prop ] :
win.document.documentElement[ method ] :
elem[ method ];
}
if ( win ) {
win.scrollTo(
!top ? val : jQuery( win ).scrollLeft(),
top ? val : jQuery( win ).scrollTop()
);
} else {
elem[ method ] = val;
}
}, method, val, arguments.length, null );
};
});
// Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// getComputedStyle returns percent when specified for top/left/bottom/right
// rather than make the css module depend on the offset module, we just check for it here
jQuery.each( [ "top", "left" ], function( i, prop ) {
jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
function( elem, computed ) {
if ( computed ) {
computed = curCSS( elem, prop );
// if curCSS returns percentage, fallback to offset
return rnumnonpx.test( computed ) ?
jQuery( elem ).position()[ prop ] + "px" :
computed;
}
}
);
});
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
// margin is only for outerHeight, outerWidth
jQuery.fn[ funcName ] = function( margin, value ) {
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
return access( this, function( elem, type, value ) {
var doc;
if ( jQuery.isWindow( elem ) ) {
// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
// isn't a whole lot we can do. See pull request at this URL for discussion:
// https://github.com/jquery/jquery/pull/764
return elem.document.documentElement[ "client" + name ];
}
// Get document width or height
if ( elem.nodeType === 9 ) {
doc = elem.documentElement;
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
return Math.max(
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
elem.body[ "offset" + name ], doc[ "offset" + name ],
doc[ "client" + name ]
);
}
return value === undefined ?
// Get width or height on the element, requesting but not forcing parseFloat
jQuery.css( elem, type, extra ) :
// Set width or height on the element
jQuery.style( elem, type, value, extra );
}, type, chainable ? margin : undefined, chainable, null );
};
});
});
// The number of elements contained in the matched element set
jQuery.fn.size = function() {
return this.length;
};
jQuery.fn.andSelf = jQuery.fn.addBack;
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
});
}
var
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$;
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( typeof noGlobal === strundefined ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
}));
/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/**
* Device and Feature Detection API of the SAP UI5 Library.
*
* @version 1.28.5
* @namespace
* @name sap.ui.Device
* @public
*/
/*global console */
//Declare Module if API is available
if (window.jQuery && window.jQuery.sap && window.jQuery.sap.declare) {
window.jQuery.sap.declare("sap.ui.Device", false);
}
//Introduce namespace if it does not yet exist
if (typeof window.sap !== "object" && typeof window.sap !== "function" ) {
window.sap = {};
}
if (typeof window.sap.ui !== "object") {
window.sap.ui = {};
}
(function() {
//Skip initialization if API is already available
if (typeof window.sap.ui.Device === "object" || typeof window.sap.ui.Device === "function" ) {
var apiVersion = "1.28.5";
window.sap.ui.Device._checkAPIVersion(apiVersion);
return;
}
var device = {};
////-------------------------- Logging -------------------------------------
/* since we cannot use the logging from jquery.sap.global.js, we need to come up with a seperate
* solution for the device API
*/
// helper function for date formatting
function pad0(i,w) {
return ("000" + String(i)).slice(-w);
}
var FATAL = 0, ERROR = 1, WARNING = 2, INFO = 3, DEBUG = 4, TRACE = 5;
var deviceLogger = function() {
this.defaultComponent = 'DEVICE';
this.sWindowName = (window.top == window) ? "" : "[" + window.location.pathname.split('/').slice(-1)[0] + "] ";
// Creates a new log entry depending on its level and component.
this.log = function (iLevel, sMessage, sComponent) {
sComponent = sComponent || this.defaultComponent || '';
var oNow = new Date(),
oLogEntry = {
time : pad0(oNow.getHours(),2) + ":" + pad0(oNow.getMinutes(),2) + ":" + pad0(oNow.getSeconds(),2),
date : pad0(oNow.getFullYear(),4) + "-" + pad0(oNow.getMonth() + 1,2) + "-" + pad0(oNow.getDate(),2),
timestamp: oNow.getTime(),
level : iLevel,
message : sMessage || "",
component: sComponent || ""
};
/*eslint-disable no-console */
if (window.console) { // in IE and FF, console might not exist; in FF it might even disappear
var logText = oLogEntry.date + " " + oLogEntry.time + " " + this.sWindowName + oLogEntry.message + " - " + oLogEntry.component;
switch (iLevel) {
case FATAL:
case ERROR: console.error(logText); break;
case WARNING: console.warn(logText); break;
case INFO: console.info ? console.info(logText) : console.log(logText); break; // info not available in iOS simulator
case DEBUG: console.debug ? console.debug(logText) : console.log(logText); break; // debug not available in IE, fallback to log
case TRACE: console.trace ? console.trace(logText) : console.log(logText); break; // trace not available in IE, fallback to log (no trace)
}
}
/*eslint-enable no-console */
return oLogEntry;
};
};
// instantiate new logger
var logger = new deviceLogger();
logger.log(INFO, "Device API logging initialized");
//******** Version Check ********
//Only used internal to make clear when Device API is loaded in wrong version
device._checkAPIVersion = function(sVersion){
var v = "1.28.5";
if (v != sVersion) {
logger.log(WARNING, "Device API version differs: " + v + " <-> " + sVersion);
}
};
//******** Event Management ******** (see Event Provider)
var mEventRegistry = {};
function attachEvent(sEventId, fnFunction, oListener) {
if (!mEventRegistry[sEventId]) {
mEventRegistry[sEventId] = [];
}
mEventRegistry[sEventId].push({oListener: oListener, fFunction:fnFunction});
}
function detachEvent(sEventId, fnFunction, oListener) {
var aEventListeners = mEventRegistry[sEventId];
if (!aEventListeners) {
return this;
}
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
if (aEventListeners[i].fFunction === fnFunction && aEventListeners[i].oListener === oListener) {
aEventListeners.splice(i,1);
break;
}
}
if (aEventListeners.length == 0) {
delete mEventRegistry[sEventId];
}
}
function fireEvent(sEventId, mParameters) {
var aEventListeners = mEventRegistry[sEventId], oInfo;
if (aEventListeners) {
aEventListeners = aEventListeners.slice();
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
oInfo = aEventListeners[i];
oInfo.fFunction.call(oInfo.oListener || window, mParameters);
}
}
}
//******** OS Detection ********
/**
* Contains information about the operating system of the device.
*
* @namespace
* @name sap.ui.Device.os
* @public
*/
/**
* Enumeration containing the names of known operating systems.
*
* @namespace
* @name sap.ui.Device.os.OS
* @public
*/
/**
* The name of the operating system.
*
* @see sap.ui.Device.os#OS
* @name sap.ui.Device.os#name
* @type String
* @public
*/
/**
* The version as string. Might be empty if no version can be determined.
*
* @name sap.ui.Device.os#versionStr
* @type String
* @public
*/
/**
* The version as float. Might be -1 if no version can be determined.
*
* @name sap.ui.Device.os#version
* @type float
* @public
*/
/**
* Flag indicating the Windows operating system.
*
* @name sap.ui.Device.os#windows
* @type boolean
* @public
*/
/**
* Flag indicating the Linux operating system.
*
* @name sap.ui.Device.os#linux
* @type boolean
* @public
*/
/**
* Flag indicating the MAC operating system.
*
* @name sap.ui.Device.os#macintosh
* @type boolean
* @public
*/
/**
* Flag indicating the iOS operating system.
*
* @name sap.ui.Device.os#ios
* @type boolean
* @public
*/
/**
* Flag indicating the Android operating system.
*
* @name sap.ui.Device.os#android
* @type boolean
* @public
*/
/**
* Flag indicating the Blackberry operating system.
*
* @name sap.ui.Device.os#blackberry
* @type boolean
* @public
*/
/**
* Flag indicating the Windows Phone operating system.
*
* @name sap.ui.Device.os#windows_phone
* @type boolean
* @public
*/
/**
* Windows operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#WINDOWS
* @public
*/
/**
* MAC operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#MACINTOSH
* @public
*/
/**
* Linux operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#LINUX
* @public
*/
/**
* iOS operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#IOS
* @public
*/
/**
* Android operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#ANDROID
* @public
*/
/**
* Blackberry operating system name.
*
* @see sap.ui.Device.os#name
* @name sap.ui.Device.os.OS#BLACKBERRY
* @public
*/
/**
* Windows Phone operating system name.
*
* @see sap.ui.Device.os#name
* @alias sap.ui.Device.os.OS#WINDOWS_PHONE
* @public
*/
var OS = {
"WINDOWS": "win",
"MACINTOSH": "mac",
"LINUX": "linux",
"IOS": "iOS",
"ANDROID": "Android",
"BLACKBERRY": "bb",
"WINDOWS_PHONE": "winphone"
};
function getOS(userAgent){ // may return null!!
userAgent = userAgent || navigator.userAgent;
var platform, // regular expression for platform
result;
function getDesktopOS(){
var pf = navigator.platform;
if (pf.indexOf("Win") != -1 ) {
// userAgent in windows 7 contains: windows NT 6.1
// userAgent in windows 8 contains: windows NT 6.2 or higher
// TODO: update this after windows 9 is released
var rVersion = /windows NT 6.(\d)/i;
var uaResult = userAgent.match(rVersion);
var sVersionStr = "";
if (uaResult) {
if (uaResult[1] == 1) {
sVersionStr = "7";
} else if (uaResult[1] > 1) {
sVersionStr = "8";
}
}
return {"name": OS.WINDOWS, "versionStr": sVersionStr};
} else if (pf.indexOf("Mac") != -1) {
return {"name": OS.MACINTOSH, "versionStr": ""};
} else if (pf.indexOf("Linux") != -1) {
return {"name": OS.LINUX, "versionStr": ""};
}
logger.log(INFO, "OS detection returned no result");
return null;
}
// Windows Phone. User agent includes other platforms and therefore must be checked first:
platform = /Windows Phone (?:OS )?([\d.]*)/;
result = userAgent.match(platform);
if (result) {
return ({"name": OS.WINDOWS_PHONE, "versionStr": result[1]});
}
// BlackBerry 10:
if (userAgent.indexOf("(BB10;") > 0) {
platform = /\sVersion\/([\d.]+)\s/;
result = userAgent.match(platform);
if (result) {
return {"name": OS.BLACKBERRY, "versionStr": result[1]};
} else {
return {"name": OS.BLACKBERRY, "versionStr": '10'};
}
}
// iOS, Android, BlackBerry 6.0+:
platform = /\(([a-zA-Z ]+);\s(?:[U]?[;]?)([\D]+)((?:[\d._]*))(?:.*[\)][^\d]*)([\d.]*)\s/;
result = userAgent.match(platform);
if (result) {
var appleDevices = /iPhone|iPad|iPod/;
var bbDevices = /PlayBook|BlackBerry/;
if (result[0].match(appleDevices)) {
result[3] = result[3].replace(/_/g, ".");
//result[1] contains info of devices
return ({"name": OS.IOS, "versionStr": result[3]});
} else if (result[2].match(/Android/)) {
result[2] = result[2].replace(/\s/g, "");
return ({"name": OS.ANDROID, "versionStr": result[3]});
} else if (result[0].match(bbDevices)) {
return ({"name": OS.BLACKBERRY, "versionStr": result[4]});
}
}
// Desktop
return getDesktopOS();
}
function setOS() {
device.os = getOS() || {};
device.os.OS = OS;
device.os.version = device.os.versionStr ? parseFloat(device.os.versionStr) : -1;
if (device.os.name) {
for (var b in OS) {
if (OS[b] === device.os.name) {
device.os[b.toLowerCase()] = true;
}
}
}
}
setOS();
//******** Browser Detection ********
/**
* Contains information about the used browser.
*
* @namespace
* @name sap.ui.Device.browser
* @public
*/
/**
* Enumeration containing the names of known browsers.
*
* @namespace
* @name sap.ui.Device.browser.BROWSER
* @public
*/
/**
* The name of the browser.
*
* @see sap.ui.Device.browser#BROWSER
* @name sap.ui.Device.browser#name
* @type String
* @public
*/
/**
* The version as string. Might be empty if no version can be determined.
*
* @name sap.ui.Device.browser#versionStr
* @type String
* @public
*/
/**
* The version as float. Might be -1 if no version can be determined.
*
* @name sap.ui.Device.browser#version
* @type float
* @public
*/
/**
* Flag indicating whether the mobile variant of the browser is used.
*
* @name sap.ui.Device.browser#mobile
* @type boolean
* @public
*/
/**
* Flag indicating the Internet Explorer browser.
*
* @name sap.ui.Device.browser#internet_explorer
* @type boolean
* @deprecated since 1.20: use sap.ui.Device.browser.msie
* @public
*/
/**
* Flag indicating the Internet Explorer browser.
*
* @name sap.ui.Device.browser#msie
* @type boolean
* @since 1.20.0
* @public
*/
/**
* Flag indicating the Firefox browser.
*
* @name sap.ui.Device.browser#firefox
* @type boolean
* @public
*/
/**
* Flag indicating the Chrome browser.
*
* @name sap.ui.Device.browser#chrome
* @type boolean
* @public
*/
/**
* Flag indicating the Safari browser.
*
* @name sap.ui.Device.browser#safari
* @type boolean
* @public
*/
/**
* Flag indicating a Webkit browser.
*
* @name sap.ui.Device.browser#webkit
* @type boolean
* @since 1.20.0
* @public
*/
/**
* Flag indicating a Mozilla browser.
*
* @name sap.ui.Device.browser#mozilla
* @type boolean
* @since 1.20.0
* @public
*/
/**
* Internet Explorer browser name.
*
* @see sap.ui.Device.browser#name
* @name sap.ui.Device.browser.BROWSER#INTERNET_EXPLORER
* @public
*/
/**
* Firefox browser name.
*
* @see sap.ui.Device.browser#name
* @name sap.ui.Device.browser.BROWSER#FIREFOX
* @public
*/
/**
* Chrome browser name.
*
* @see sap.ui.Device.browser#name
* @name sap.ui.Device.browser.BROWSER#CHROME
* @public
*/
/**
* Safari browser name.
*
* @see sap.ui.Device.browser#name
* @name sap.ui.Device.browser.BROWSER#SAFARI
* @public
*/
/**
* Android stock browser name.
*
* @see sap.ui.Device.browser#name
* @alias sap.ui.Device.browser.BROWSER#ANDROID
* @public
*/
var BROWSER = {
"INTERNET_EXPLORER": "ie",
"FIREFOX": "ff",
"CHROME": "cr",
"SAFARI": "sf",
"ANDROID": "an"
};
var ua = navigator.userAgent;
/*!
* Taken from jQuery JavaScript Library v1.7.1
* http://jquery.com/
*
* Copyright 2011, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Mon Nov 21 21:11:03 2011 -0500
*/
function calcBrowser(customUa){
var _ua = (customUa || ua).toLowerCase(); // use custom user-agent if given
var rwebkit = /(webkit)[ \/]([\w.]+)/;
var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
var rmsie = /(msie) ([\w.]+)/;
//TODO this might needs to be adjusted in future IE version > 11
var rmsienew = /(trident)\/[\w.]+;.*rv:([\w.]+)/;
var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
// WinPhone IE11 userAgent contains "WebKit" and "Mozilla" and therefore must be checked first
var browserMatch = rmsienew.exec( _ua ) ||
rwebkit.exec( _ua ) ||
ropera.exec( _ua ) ||
rmsie.exec( _ua ) ||
_ua.indexOf("compatible") < 0 && rmozilla.exec( _ua ) ||
[];
var res = { browser: browserMatch[1] || "", version: browserMatch[2] || "0" };
res[res.browser] = true;
return res;
}
function getBrowser(customUa) {
var b = calcBrowser(customUa);
var _ua = customUa || ua;
// jQuery checks for user agent strings. We differentiate between browsers
var oExpMobile;
if ( b.mozilla ) {
oExpMobile = /Mobile/;
if ( _ua.match(/Firefox\/(\d+\.\d+)/) ) {
var version = parseFloat(RegExp.$1);
return {
name: BROWSER.FIREFOX,
versionStr: "" + version,
version: version,
mozilla: true,
mobile: oExpMobile.test(_ua)
};
} else {
// unknown mozilla browser
return {
mobile: oExpMobile.test(_ua),
mozilla: true
};
}
} else if ( b.webkit ) {
// webkit version is needed for calculation if the mobile android device is a tablet (calculation of other mobile devices work without)
var regExpWebkitVersion = _ua.toLowerCase().match(/webkit[\/]([\d.]+)/);
var webkitVersion;
if (regExpWebkitVersion) {
webkitVersion = regExpWebkitVersion[1];
}
oExpMobile = /Mobile/;
if ( _ua.match(/(Chrome|CriOS)\/(\d+\.\d+).\d+/)) {
var version = parseFloat(RegExp.$2);
return {
name: BROWSER.CHROME,
versionStr: "" + version,
version: version,
mobile: oExpMobile.test(_ua),
webkit: true,
webkitVersion: webkitVersion
};
} else if ( _ua.match(/Android .+ Version\/(\d+\.\d+)/) ) {
var version = parseFloat(RegExp.$1);
return {
name: BROWSER.ANDROID,
versionStr: "" + version,
version: version,
mobile: oExpMobile.test(_ua),
webkit: true,
webkitVersion: webkitVersion
};
} else { // Safari might have an issue with _ua.match(...); thus changing
var oExp = /(Version|PhantomJS)\/(\d+\.\d+).*Safari/;
if (oExp.test(_ua)) {
var aParts = oExp.exec(_ua);
var version = parseFloat(aParts[2]);
return {
name: BROWSER.SAFARI,
versionStr: "" + version,
version: version,
mobile: oExpMobile.test(_ua),
webkit: true,
webkitVersion: webkitVersion,
phantomJS: aParts[1] === "PhantomJS"
};
} else {
// unknown webkit browser
return {
mobile: oExpMobile.test(_ua),
webkit: true,
webkitVersion: webkitVersion
};
}
}
} else if ( b.msie || b.trident ) {
var version;
// recognize IE8 when running in compat mode (only then the documentMode property is there)
if (document.documentMode && !customUa) { // only use the actual documentMode when no custom user-agent was given
if (document.documentMode === 7) { // OK, obviously we are IE and seem to be 7... but as documentMode is there this cannot be IE7!
version = 8.0;
} else {
version = parseFloat(document.documentMode);
}
} else {
version = parseFloat(b.version);
}
return {
name: BROWSER.INTERNET_EXPLORER,
versionStr: "" + version,
version: version,
msie: true,
mobile: false // TODO: really?
};
}
return {
name: "",
versionStr: "",
version: -1,
mobile: false
};
}
device._testUserAgent = getBrowser; // expose the user-agent parsing (mainly for testing), but don't let it be overwritten
function setBrowser() {
device.browser = getBrowser();
device.browser.BROWSER = BROWSER;
if (device.browser.name) {
for (var b in BROWSER) {
if (BROWSER[b] === device.browser.name) {
device.browser[b.toLowerCase()] = true;
}
}
}
}
setBrowser();
//******** Support Detection ********
/**
* Contains information about detected capabilities of the used browser or device.
*
* @namespace
* @name sap.ui.Device.support
* @public
*/
/**
* Flag indicating whether touch events are supported.
*
* @name sap.ui.Device.support#touch
* @type boolean
* @public
*/
/**
* Flag indicating whether pointer events are supported.
*
* @name sap.ui.Device.support#pointer
* @type boolean
* @public
*/
/**
* Flag indicating whether media queries via JavaScript are supported.
*
* @name sap.ui.Device.support#matchmedia
* @type boolean
* @public
*/
/**
* Flag indicating whether events on JavaScript media queries are supported.
*
* @name sap.ui.Device.support#matchmedialistener
* @type boolean
* @public
*/
/**
* Flag indicating whether the native orientationchange event is supported.
*
* @name sap.ui.Device.support#orientation
* @type boolean
* @public
*/
/**
* Flag indicating whether the device has a Retina display.
*
* @name sap.ui.Device.support#retina
* @type boolean
* @public
*/
/**
* Flag indicating whether WebSockets are supported.
*
* @name sap.ui.Device.support#websocket
* @type boolean
* @public
*/
/**
* Flag indicating whether placeholder on input tags are supported.
*
* @name sap.ui.Device.support#input.placeholder
* @type boolean
* @public
*/
device.support = {};
//Maybe better to but this on device.browser because there are cases that a browser can touch but a device can't!
device.support.touch = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch);
// FIXME: PhantomJS doesn't support touch events but exposes itself as touch
// enabled browser. Therfore we manually override that in jQuery.support!
// This has been tested with PhantomJS 1.9.7 and 2.0.0!
if (device.browser.phantomJS) {
device.support.touch = false;
}
device.support.pointer = !!window.PointerEvent;
device.support.matchmedia = !!window.matchMedia;
var m = device.support.matchmedia ? window.matchMedia("screen and (max-width:0px)") : null; //IE10 doesn't like empty string as argument for matchMedia, FF returns null when running within an iframe with display:none
device.support.matchmedialistener = !!(m && m.addListener);
if (device.browser.safari && device.browser.version < 6) {
//Safari seems to have addListener but no events are fired ?!
device.support.matchmedialistener = false;
}
device.support.orientation = !!("orientation" in window && "onorientationchange" in window);
device.support.retina = (window.retina || window.devicePixelRatio >= 2);
device.support.websocket = ('WebSocket' in window);
device.support.input = {};
device.support.input.placeholder = ('placeholder' in document.createElement("input"));
//******** Match Media ********
/**
* Event API for Screen width media queries.
*
* @namespace
* @name sap.ui.Device.media
* @public
*/
device.media = {};
/**
* Enumeration containing the names of predefined Screen width media query range sets.
*
* @namespace
* @name sap.ui.Device.media.RANGESETS
* @public
*/
/**
* A 3 step range set (S-L).
*
* @name sap.ui.Device.media.RANGESETS#SAP_3STEPS
* @public
*/
/**
* A 4 step range set (S-XL).
*
* @name sap.ui.Device.media.RANGESETS#SAP_4STEPS
* @public
*/
/**
* A 6 step range set (XS-XXL).
*
* @name sap.ui.Device.media.RANGESETS#SAP_6STEPS
* @public
*/
/**
* A 3 step range set (Phone, Tablet, Desktop). <br/>
* <br/>
* This range set is initialized always by default.<br/>
* Phone is < 600px<br/>
* Tablet is 600px >= Tablet < 1024<br/>
* Desktop is > 1024px<br/>
* <br/>
* There are 5 css classes to hide elements based on the width of the screen:
* <ul>
* <li>sapUiHideOnPhone - will be hidden if the screen has 600px or more</li>
* <li>sapUiHideOnTablet - will be hidden if the screen has less than 600px or more than 1023px</li>
* <li>sapUiHideOnDesktop - will be hidden if the screen is smaller than 1024px</li>
* <li>sapUiVisibleOnlyOnPhone - will be visible if the screen has less than 600px</li>
* <li>sapUiVisibleOnlyOnTablet - will be visible if the screen has 600px or more but less than 1024px</li>
* <li>sapUiVisibleOnlyOnDesktop - will be visible if the screen has 1024px or more</li>
* </ul>
* @alias sap.ui.Device.media.RANGESETS#SAP_STANDARD
* @public
*/
var RANGESETS = {
"SAP_3STEPS": "3Step",
"SAP_4STEPS": "4Step",
"SAP_6STEPS": "6Step",
"SAP_STANDARD": "Std"
};
device.media.RANGESETS = RANGESETS;
device.media._predefinedRangeSets = {};
device.media._predefinedRangeSets[RANGESETS.SAP_3STEPS] = {points: [520, 960], unit: "px", name: RANGESETS.SAP_3STEPS, names: ["S", "M", "L"]};
device.media._predefinedRangeSets[RANGESETS.SAP_4STEPS] = {points: [520, 760, 960], unit: "px", name: RANGESETS.SAP_4STEPS, names: ["S", "M", "L", "XL"]};
device.media._predefinedRangeSets[RANGESETS.SAP_6STEPS] = {points: [241, 400, 541, 768, 960], unit: "px", name: RANGESETS.SAP_6STEPS, names: ["XS", "S", "M", "L", "XL", "XXL"]};
device.media._predefinedRangeSets[RANGESETS.SAP_STANDARD] = {points: [600, 1024], unit: "px", name: RANGESETS.SAP_STANDARD, names: ["Phone", "Tablet", "Desktop"]};
var _defaultRangeSet = RANGESETS.SAP_STANDARD;
var media_timeout = device.support.matchmedialistener ? 0 : 100;
var _querysets = {};
var media_currentwidth = null;
function getQuery(from, to, unit){
unit = unit || "px";
var q = "screen";
if (from > 0) {
q = q + " and (min-width:" + from + unit + ")";
}
if (to > 0) {
q = q + " and (max-width:" + to + unit + ")";
}
return q;
}
function handleChange(name){
if (!device.support.matchmedialistener && media_currentwidth == windowSize()[0]) {
return; //Skip unnecessary resize events
}
if (_querysets[name].timer) {
clearTimeout(_querysets[name].timer);
_querysets[name].timer = null;
}
_querysets[name].timer = setTimeout(function() {
var mParams = checkQueries(name, false);
if (mParams) {
fireEvent("media_" + name, mParams);
}
}, media_timeout);
}
function getRangeInfo(sSetName, iRangeIdx){
var q = _querysets[sSetName].queries[iRangeIdx];
var info = {from: q.from, unit: _querysets[sSetName].unit};
if (q.to >= 0) {
info.to = q.to;
}
if (_querysets[sSetName].names) {
info.name = _querysets[sSetName].names[iRangeIdx];
}
return info;
}
function checkQueries(name, infoOnly){
if (_querysets[name]) {
var aQueries = _querysets[name].queries;
var info = null;
for (var i = 0, len = aQueries.length; i < len; i++) {
var q = aQueries[i];
if ((q != _querysets[name].currentquery || infoOnly) && device.media.matches(q.from, q.to, _querysets[name].unit)) {
if (!infoOnly) {
_querysets[name].currentquery = q;
}
if (!_querysets[name].noClasses && _querysets[name].names && !infoOnly) {
refreshCSSClasses(name, _querysets[name].names[i]);
}
info = getRangeInfo(name, i);
}
}
return info;
}
logger.log(WARNING, "No queryset with name " + name + " found", 'DEVICE.MEDIA');
return null;
}
function refreshCSSClasses(sSetName, sRangeName, bRemove){
var sClassPrefix = "sapUiMedia-" + sSetName + "-";
changeRootCSSClass(sClassPrefix + sRangeName, bRemove, sClassPrefix);
}
function changeRootCSSClass(sClassName, bRemove, sPrefix){
var oRoot = document.documentElement;
if (oRoot.className.length == 0) {
if (!bRemove) {
oRoot.className = sClassName;
}
} else {
var aCurrentClasses = oRoot.className.split(" ");
var sNewClasses = "";
for (var i = 0; i < aCurrentClasses.length; i++) {
if ((sPrefix && aCurrentClasses[i].indexOf(sPrefix) != 0) || (!sPrefix && aCurrentClasses[i] != sClassName)) {
sNewClasses = sNewClasses + aCurrentClasses[i] + " ";
}
}
if (!bRemove) {
sNewClasses = sNewClasses + sClassName;
}
oRoot.className = sNewClasses;
}
}
function windowSize(){
return [document.documentElement.clientWidth, document.documentElement.clientHeight];
}
function convertToPx(val, unit){
if (unit === "em" || unit === "rem") {
var s = window.getComputedStyle || function(e) {
return e.currentStyle;
};
var x = s(document.documentElement).fontSize;
var f = (x && x.indexOf("px") >= 0) ? parseFloat(x, 10) : 16;
return val * f;
}
return val;
}
function match_legacy(from, to, unit){
from = convertToPx(from, unit);
to = convertToPx(to, unit);
var width = windowSize()[0];
var a = from < 0 || from <= width;
var b = to < 0 || width <= to;
return a && b;
}
function match(from, to, unit){
var q = getQuery(from, to, unit);
var mm = window.matchMedia(q); //FF returns null when running within an iframe with display:none
return mm && mm.matches;
}
device.media.matches = device.support.matchmedia ? match : match_legacy;
/**
* Registers the given handler to the range change event, which is fired when a new range of the set is entered.
*
* The handler has one map parameter <code>mParams</code>:
* <ul>
* <li>mParams.from: the range start value</li>
* <li>mParams.to: the range end value, not defined for the last range (INFINITY)</li>
* <li>mParams.unit: the used unit, e.g. px</li>
* <li>mParams.name: the range name if defined</li>
* </ul>
*
* @param {Function} fnFunction The function to call, when the range change event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @param {String} sName The name of the range set to listen to.
* @name sap.ui.Device.media#attachHandler
* @function
* @public
*/
device.media.attachHandler = function(fnFunction, oListener, sName){
var name = sName || _defaultRangeSet;
attachEvent("media_" + name, fnFunction, oListener);
};
/**
* Deregisters a previously registered handler from the range change event.
*
* @param {Function} fnFunction The function to call, when the range change event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @param {String} sName The name of the range set to listen to.
* @name sap.ui.Device.media#detachHandler
* @function
* @public
*/
device.media.detachHandler = function(fnFunction, oListener, sName){
var name = sName || _defaultRangeSet;
detachEvent("media_" + name, fnFunction, oListener);
};
/**
* Initializes a Screen width media query range set.
*
* This function can either be called only with the name parameter to initialize a predefined range set,
* e.g. <code>sap.ui.Device.media.initRangeSet(sap.ui.Device.media.RANGESETS.SAP_3STEPS)</code>.
*
* Or it is possible to define a custom range set as in the following example:
* <code>sap.ui.Device.media.initRangeSet("MyRangeSet", [200, 400], "px", ["Small", "Medium", "Large"])</code> defines 3 ranges:
* <ul>
* <li>0px-199.999px with name "Small"</li>
* <li>200px-399.999px with name "Medium"</li>
* <li>400px-INFINITY with name "Large"</li>
* </ul>
*
* The range names are optional. If they are specified also a CSS class (e.g. sapUiMedia-MyRangeSet-Small) is added to the document root
* depending on the current active range. This can be suppressed via parameter <code>bSuppressClasses</code>.
*
* @param {String} sName The name of the range set. Either a predefined or custom one. The name must be a valid id (consist of letters and digits).
* @param {int[]} aRangeBorders The range borders
* @param {String} [sUnit] The unit which should be used. Allowed values are px (default), em or rem.
* @param {String[]} [aRangeNames] The names of the ranges. The names must be a valid id (consist of letters and digits).
* @param {boolean} [bSuppressClasses] Whether writing CSS classes to the document root should be suppressed
* @name sap.ui.Device.media#initRangeSet
* @function
* @public
*/
device.media.initRangeSet = function(sName, aRangeBorders, sUnit, aRangeNames, bSuppressClasses){
//TODO Do some Assertions and parameter checking
var oConfig;
if (!sName) {
oConfig = device.media._predefinedRangeSets[_defaultRangeSet];
} else if (sName && device.media._predefinedRangeSets[sName]) {
oConfig = device.media._predefinedRangeSets[sName];
} else {
oConfig = {name: sName, unit: (sUnit || "px").toLowerCase(), points: aRangeBorders || [], names: aRangeNames, noClasses: !!bSuppressClasses};
}
if (device.media.hasRangeSet(oConfig.name)) {
logger.log(INFO, "Range set " + oConfig.name + " hase already been initialized", 'DEVICE.MEDIA');
return;
}
sName = oConfig.name;
oConfig.queries = [];
oConfig.timer = null;
oConfig.currentquery = null;
oConfig.listener = function(){
return handleChange(sName);
};
var from, to, query;
var aPoints = oConfig.points;
for (var i = 0, len = aPoints.length; i <= len; i++) {
from = (i == 0) ? 0 : aPoints[i - 1];
to = (i == aPoints.length) ? -1 : aPoints[i];
query = getQuery(from, to, oConfig.unit);
oConfig.queries.push({
query: query,
from: from,
to: to
});
}
if (oConfig.names && oConfig.names.length != oConfig.queries.length) {
oConfig.names = null;
}
_querysets[oConfig.name] = oConfig;
if (device.support.matchmedialistener) { //FF, Safari, Chrome, IE10?
var queries = oConfig.queries;
for (var i = 0; i < queries.length; i++) {
var q = queries[i];
q.media = window.matchMedia(q.query);
q.media.addListener(oConfig.listener);
}
} else { //IE, Safari (<6?)
if (window.addEventListener) {
window.addEventListener("resize", oConfig.listener, false);
window.addEventListener("orientationchange", oConfig.listener, false);
} else { //IE8
window.attachEvent("onresize", oConfig.listener);
}
}
oConfig.listener();
};
/**
* Returns information about the current active range of the range set with the given name.
*
* @param {String} sName The name of the range set.
* @name sap.ui.Device.media#getCurrentRange
* @return {Map} the information about the current active range (same structure like the handler parameters (@see sap.ui.Device.media#attachHandler))
* @function
* @public
*/
device.media.getCurrentRange = function(sName){
if (!device.media.hasRangeSet(sName)) {
return null;
}
return checkQueries(sName, true);
};
/**
* Returns whether a range set with the given name is initialized.
*
* @param {String} sName The name of the range set.
* @name sap.ui.Device.media#hasRangeSet
* @return {boolean}
* @function
* @public
*/
device.media.hasRangeSet = function(sName){
return sName && !!_querysets[sName];
};
/**
* Removes a previously initialized range set and detaches all registered handlers.
*
* Initialized predefined range sets (@see sap.ui.Device.media#RANGESETS) cannot be removed.
*
* @param {String} sName The name of the range set.
* @name sap.ui.Device.media#removeRangeSet
* @function
* @protected
*/
device.media.removeRangeSet = function(sName){
if (!device.media.hasRangeSet(sName)) {
logger.log(INFO, "RangeSet " + sName + " not found, thus could not be removed.", 'DEVICE.MEDIA');
return;
}
for (var x in RANGESETS) {
if (sName === RANGESETS[x]) {
logger.log(WARNING, "Cannot remove default rangeset - no action taken.", 'DEVICE.MEDIA');
return;
}
}
var oConfig = _querysets[sName];
if (device.support.matchmedialistener) { //FF, Safari, Chrome, IE10?
var queries = oConfig.queries;
for (var i = 0; i < queries.length; i++) {
queries[i].media.removeListener(oConfig.listener);
}
} else { //IE, Safari (<6?)
if (window.removeEventListener) {
window.removeEventListener("resize", oConfig.listener, false);
window.removeEventListener("orientationchange", oConfig.listener, false);
} else { //IE8
window.detachEvent("onresize", oConfig.listener);
}
}
refreshCSSClasses(sName, "", true);
delete mEventRegistry["media_" + sName];
delete _querysets[sName];
};
//******** System Detection ********
/**
* Contains information about the system.
*
* @namespace
* @name sap.ui.Device.system
* @public
*/
/**
* Enumeration containing the names of known types of the devices.
*
* @namespace
* @name sap.ui.Device.system.SYSTEMTYPE
* @public
*/
/**
* Flag indicating if the device is a tablet.
*
* @name sap.ui.Device.system#tablet
* @type boolean
* @public
*/
/**
* Flag indicating if the device is a phone.
*
* @name sap.ui.Device.system#phone
* @type boolean
* @public
*/
/**
* Flag indicating if the device is a desktop.
*
* @name sap.ui.Device.system#desktop
* @type boolean
* @public
*/
/**
* Flag indicating if the device is a combination of desktop and tablet.
*
* This property is mainly targeting the windows 8 devices where the mouse and touch event may supported
* natively by the browser.
*
* This property is set to true only when both mouse and touch event are natively supported.
*
* @alias sap.ui.Device.system#combi
* @type boolean
* @public
*/
var SYSTEMTYPE = {
"TABLET" : "tablet",
"PHONE" : "phone",
"DESKTOP" : "desktop",
"COMBI" : "combi"
};
var isWin8 = !!device.os.windows && device.os.version === 8;
var isWin7 = !!device.os.windows && device.os.version === 7;
device.system = {};
function getSystem(_simMobileOnDesktop) {
var t = isTablet();
var s = {};
s.tablet = ((device.support.touch && !isWin7) || isWin8 || !!_simMobileOnDesktop) && t;
s.phone = device.os.windows_phone || ((device.support.touch && !isWin7) || !!_simMobileOnDesktop) && !t;
s.desktop = (!s.tablet && !s.phone) || isWin8 || isWin7;
s.combi = (s.desktop && s.tablet);
s.SYSTEMTYPE = SYSTEMTYPE;
for (var type in SYSTEMTYPE) {
changeRootCSSClass("sap-" + SYSTEMTYPE[type], !s[SYSTEMTYPE[type]]);
}
return s;
}
function isTablet() {
var android_phone = (/(?=android)(?=.*mobile)/i.test(navigator.userAgent));
// According to google documentation: https://developer.chrome.com/multidevice/webview/overview, the WebView shipped with Android 4.4 (KitKat) is based on the same code as Chrome for Android.
// If you're attempting to differentiate between the WebView and Chrome for Android, you should look for the presence of the Version/_X.X_ string in the WebView user-agent string
// The stock browser of Samsung device uses Chrome kernal from Android version 4.4. It behaves differently than the Chrome Webview, therefore it's excluded from this check by checking the 'SAMSUNG'
// string in the user agent.
var bChromeWebView = device.os.android && device.browser.chrome && (device.os.version >= 4.4) && /Version\/\d.\d/.test(navigator.userAgent) && !/SAMSUNG/.test(navigator.userAgent);
if (device.os.name === device.os.OS.IOS) {
return /ipad/i.test(navigator.userAgent);
} else {
if (device.support.touch) {
if (isWin8) {
return true;
}
//in real mobile device
var densityFactor = window.devicePixelRatio ? window.devicePixelRatio : 1; // may be undefined in Windows Phone devices
if (!bChromeWebView && (device.os.name === device.os.OS.ANDROID) && device.browser.webkit && (device.browser.webkitVersion > 537.10)) {
// On Android sometimes window.screen.width returns the logical CSS pixels, sometimes the physical device pixels;
// Tests on multiple devices suggest this depends on the Webkit version.
// The Webkit patch which changed the behavior was done here: https://bugs.webkit.org/show_bug.cgi?id=106460
// Chrome 27 with Webkit 537.36 returns the logical pixels,
// Chrome 18 with Webkit 535.19 returns the physical pixels.
// The BlackBerry 10 browser with Webkit 537.10+ returns the physical pixels.
// So it appears like somewhere above Webkit 537.10 we do not hve to divide by the devicePixelRatio anymore.
// update: Chrome WebView returns physical pixels therefore it's excluded from this special check
densityFactor = 1;
}
//this is how android distinguishes between tablet and phone
//http://android-developers.blogspot.de/2011/07/new-tools-for-managing-screen-sizes.html
var bTablet = (Math.min(window.screen.width / densityFactor, window.screen.height / densityFactor) >= 600);
// special workaround for Nexus 7 where the window.screen.width is 600px or 601px in portrait mode (=> tablet)
// but window.screen.height 552px in landscape mode (=> phone), because the browser UI takes some space on top.
// So the detected device type depends on the orientation :-(
// actually this is a Chrome bug, as "width"/"height" should return the entire screen's dimensions and
// "availWidth"/"availHeight" should return the size available after subtracting the browser UI
if (isLandscape()
&& (window.screen.height === 552 || window.screen.height === 553) // old/new Nexus 7
&& (/Nexus 7/i.test(navigator.userAgent))) {
bTablet = true;
}
return bTablet;
} else {
// in desktop browser, it's detected as tablet when
// 1. Windows 8 device with a touch screen where "Touch" is contained in the userAgent
// 2. Android emulation and it's not an Android phone
return (device.browser.msie && ua.indexOf("Touch") !== -1) || (device.os.name === device.os.OS.ANDROID && !android_phone);
}
}
}
function setSystem(_simMobileOnDesktop) {
device.system = getSystem(_simMobileOnDesktop);
if (device.system.tablet || device.system.phone) {
device.browser.mobile = true;
}
}
setSystem();
//******** Orientation Detection ********
/**
* Orientation Change Event API.
*
* @namespace
* @name sap.ui.Device.orientation
* @public
*/
device.orientation = {};
/**
* Resize Event API.
*
* @namespace
* @name sap.ui.Device.resize
* @public
*/
device.resize = {};
/**
* Registers the given handler to the orientation change event.
*
* The handler has one map parameter <code>mParams</code>:
* <ul>
* <li>mParams.landscape: whether the orientation is currently landscape</li>
* </ul>
*
* @param {Function} fnFunction The function to call, when the orientation change event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @name sap.ui.Device.orientation#attachHandler
* @function
* @public
*/
device.orientation.attachHandler = function(fnFunction, oListener){
attachEvent("orientation", fnFunction, oListener);
};
/**
* Registers the given handler to the resize event.
*
* The handler has one map parameter <code>mParams</code>:
* <ul>
* <li>mParams.height: new height of the window</li>
* <li>mParams.width: new width of the window</li>
* </ul>
*
* @param {Function} fnFunction The function to call, when the resize event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @name sap.ui.Device.resize#attachHandler
* @function
* @public
*/
device.resize.attachHandler = function(fnFunction, oListener){
attachEvent("resize", fnFunction, oListener);
};
/**
* Deregisters a previously registered handler from the orientation change event.
* @param {Function} fnFunction The function to call, when the orientation change event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @name sap.ui.Device.orientation#detachHandler
* @function
* @public
*/
device.orientation.detachHandler = function(fnFunction, oListener){
detachEvent("orientation", fnFunction, oListener);
};
/**
* Deregisters a previously registered handler from the resize event.
* @param {Function} fnFunction The function to call, when the resize event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @name sap.ui.Device.resize#detachHandler
* @function
* @public
*/
device.resize.detachHandler = function(fnFunction, oListener){
detachEvent("resize", fnFunction, oListener);
};
function setOrientationInfo(oInfo){
oInfo.landscape = isLandscape(true);
oInfo.portrait = !oInfo.landscape;
}
function handleOrientationChange(){
setOrientationInfo(device.orientation);
fireEvent("orientation", {landscape: device.orientation.landscape});
}
function handleResizeChange(){
setResizeInfo(device.resize);
fireEvent("resize", {height: device.resize.height, width: device.resize.width});
}
function setResizeInfo(oInfo){
oInfo.width = windowSize()[0];
oInfo.height = windowSize()[1];
}
function handleOrientationResizeChange(){
var wasL = device.orientation.landscape;
var isL = isLandscape();
if (wasL != isL) {
handleOrientationChange();
}
//throttle resize events because most browsers throw one or more resize events per pixel
//for every resize event inside the period from 150ms (starting from the first resize event),
//we only fire one resize event after this period
if (!iResizeTimeout) {
iResizeTimeout = window.setTimeout(handleResizeTimeout, 150);
}
}
function handleResizeTimeout() {
handleResizeChange();
iResizeTimeout = null;
}
var bOrientationchange = false;
var bResize = false;
var iOrientationTimeout;
var iResizeTimeout;
var iClearFlagTimeout;
var iWindowHeightOld = windowSize()[1];
var iWindowWidthOld = windowSize()[0];
var bKeyboardOpen = false;
var iLastResizeTime;
var rInputTagRegex = /INPUT|TEXTAREA|SELECT/;
// On iPhone with iOS version 7.0.x and on iPad with iOS version 7.x (tested with all versions below 7.1.1), there's a invalide resize event fired
// when changing the orientation while keyboard is shown.
var bSkipFirstResize = device.os.ios && device.browser.name === "sf" &&
((device.system.phone && device.os.version >= 7 && device.os.version < 7.1) || (device.system.tablet && device.os.version >= 7));
function isLandscape(bFromOrientationChange){
if (device.support.touch && device.support.orientation) {
//if on screen keyboard is open and the call of this method is from orientation change listener, reverse the last value.
//this is because when keyboard opens on android device, the height can be less than the width even in portrait mode.
if (bKeyboardOpen && bFromOrientationChange) {
return !device.orientation.landscape;
}
//when keyboard opens, the last orientation change value will be retured.
if (bKeyboardOpen) {
return device.orientation.landscape;
}
//otherwise compare the width and height of window
} else {
//most desktop browsers and windows phone/tablet which not support orientationchange
if (device.support.matchmedia && device.support.orientation) {
return !!window.matchMedia("(orientation: landscape)").matches;
}
}
var size = windowSize();
return size[0] > size[1];
}
function handleMobileOrientationResizeChange(evt) {
if (evt.type == "resize") {
// supress the first invalid resize event fired before orientationchange event while keyboard is open on iPhone 7.0.x
// because this event has wrong size infos
if (bSkipFirstResize && rInputTagRegex.test(document.activeElement.tagName) && !bOrientationchange) {
return;
}
var iWindowHeightNew = windowSize()[1];
var iWindowWidthNew = windowSize()[0];
var iTime = new Date().getTime();
//skip multiple resize events by only one orientationchange
if (iWindowHeightNew === iWindowHeightOld && iWindowWidthNew === iWindowWidthOld) {
return;
}
bResize = true;
//on mobile devices opening the keyboard on some devices leads to a resize event
//in this case only the height changes, not the width
if ((iWindowHeightOld != iWindowHeightNew) && (iWindowWidthOld == iWindowWidthNew)) {
//Asus Transformer tablet fires two resize events when orientation changes while keyboard is open.
//Between these two events, only the height changes. The check of if keyboard is open has to be skipped because
//it may be judged as keyboard closed but the keyboard is still open which will affect the orientation detection
if (!iLastResizeTime || (iTime - iLastResizeTime > 300)) {
bKeyboardOpen = (iWindowHeightNew < iWindowHeightOld);
}
handleResizeChange();
} else {
iWindowWidthOld = iWindowWidthNew;
}
iLastResizeTime = iTime;
iWindowHeightOld = iWindowHeightNew;
if (iClearFlagTimeout) {
window.clearTimeout(iClearFlagTimeout);
iClearFlagTimeout = null;
}
//Some Android build-in browser fires a resize event after the viewport is applied.
//This resize event has to be dismissed otherwise when the next orientationchange event happens,
//a UI5 resize event will be fired with the wrong window size.
iClearFlagTimeout = window.setTimeout(clearFlags, 1200);
} else if (evt.type == "orientationchange") {
bOrientationchange = true;
}
if (iOrientationTimeout) {
clearTimeout(iOrientationTimeout);
iOrientationTimeout = null;
}
iOrientationTimeout = window.setTimeout(handleMobileTimeout, 50);
}
function handleMobileTimeout() {
if (bOrientationchange && bResize) {
handleOrientationChange();
handleResizeChange();
bOrientationchange = false;
bResize = false;
if (iClearFlagTimeout) {
window.clearTimeout(iClearFlagTimeout);
iClearFlagTimeout = null;
}
}
iOrientationTimeout = null;
}
function clearFlags(){
bOrientationchange = false;
bResize = false;
iClearFlagTimeout = null;
}
//******** Update browser settings for test purposes ********
device._update = function(_simMobileOnDesktop) {
ua = navigator.userAgent;
logger.log(WARNING, "Device API values manipulated: NOT PRODUCTIVE FEATURE!!! This should be only used for test purposes. Only use if you know what you are doing.");
setBrowser();
setOS();
setSystem(_simMobileOnDesktop);
};
//********************************************************
setResizeInfo(device.resize);
setOrientationInfo(device.orientation);
//Add API to global namespace
window.sap.ui.Device = device;
// Add handler for orientationchange and resize after initialization of Device API (IE8 fires onresize synchronously)
if (device.support.touch && device.support.orientation) {
//logic for mobile devices which support orientationchange (like ios, android, blackberry)
window.addEventListener("resize", handleMobileOrientationResizeChange, false);
window.addEventListener("orientationchange", handleMobileOrientationResizeChange, false);
} else {
if (window.addEventListener) {
//most desktop browsers and windows phone/tablet which not support orientationchange
window.addEventListener("resize", handleOrientationResizeChange, false);
} else {
//IE8
window.attachEvent("onresize", handleOrientationResizeChange);
}
}
//Always initialize the default media range set
device.media.initRangeSet();
// define module if API is available
if (sap.ui.define) {
sap.ui.define("sap/ui/Device", [], function() {
return device;
});
}
}());
/*!
* URI.js - Mutating URLs
*
* Version: 1.11.2
*
* Author: Rodney Rehm
* Web: http://medialize.github.com/URI.js/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
// ##### BEGIN: MODIFIED BY SAP
// define(['./punycode', './IPv6', './SecondLevelDomains'], factory);
// we can't support loading URI.js via AMD define. URI.js is packaged with SAPUI5 code
// and define() doesn't execute synchronously. So the UI5 code executed after URI.js
// fails as it is missing the URI.js code.
// Instead we use the standard init code and only expose the result via define()
// The (optional) dependencies are lost or must be loaded in advance
root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
define([], function() { return root.URI; });
// ##### END: MODIFIED BY SAP
} else {
// Browser globals (root is window)
root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
}
}(this, function (punycode, IPv6, SLD, root) {
"use strict";
// save current URI variable, if any
var _URI = root && root.URI;
function URI(url, base) {
// Allow instantiation without the 'new' keyword
if (!(this instanceof URI)) {
return new URI(url, base);
}
if (url === undefined) {
if (typeof location !== 'undefined') {
url = location.href + "";
} else {
url = "";
}
}
this.href(url);
// resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor
if (base !== undefined) {
return this.absoluteTo(base);
}
return this;
};
var p = URI.prototype;
var hasOwn = Object.prototype.hasOwnProperty;
function escapeRegEx(string) {
// https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963
return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
function getType(value) {
// IE8 doesn't return [Object Undefined] but [Object Object] for undefined value
if (value === undefined) {
return 'Undefined';
}
return String(Object.prototype.toString.call(value)).slice(8, -1);
}
function isArray(obj) {
return getType(obj) === "Array";
}
function filterArrayValues(data, value) {
var lookup = {};
var i, length;
if (isArray(value)) {
for (i = 0, length = value.length; i < length; i++) {
lookup[value[i]] = true;
}
} else {
lookup[value] = true;
}
for (i = 0, length = data.length; i < length; i++) {
if (lookup[data[i]] !== undefined) {
data.splice(i, 1);
length--;
i--;
}
}
return data;
}
function arrayContains(list, value) {
var i, length;
// value may be string, number, array, regexp
if (isArray(value)) {
// Note: this can be optimized to O(n) (instead of current O(m * n))
for (i = 0, length = value.length; i < length; i++) {
if (!arrayContains(list, value[i])) {
return false;
}
}
return true;
}
var _type = getType(value);
for (i = 0, length = list.length; i < length; i++) {
if (_type === 'RegExp') {
if (typeof list[i] === 'string' && list[i].match(value)) {
return true;
}
} else if (list[i] === value) {
return true;
}
}
return false;
}
function arraysEqual(one, two) {
if (!isArray(one) || !isArray(two)) {
return false;
}
// arrays can't be equal if they have different amount of content
if (one.length !== two.length) {
return false;
}
one.sort();
two.sort();
for (var i = 0, l = one.length; i < l; i++) {
if (one[i] !== two[i]) {
return false;
}
}
return true;
}
URI._parts = function() {
return {
protocol: null,
username: null,
password: null,
hostname: null,
urn: null,
port: null,
path: null,
query: null,
fragment: null,
// state
duplicateQueryParameters: URI.duplicateQueryParameters,
escapeQuerySpace: URI.escapeQuerySpace
};
};
// state: allow duplicate query parameters (a=1&a=1)
URI.duplicateQueryParameters = false;
// state: replaces + with %20 (space in query strings)
URI.escapeQuerySpace = true;
// static properties
URI.protocol_expression = /^[a-z][a-z0-9-+-]*$/i;
URI.idn_expression = /[^a-z0-9\.-]/i;
URI.punycode_expression = /(xn--)/i;
// well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care?
URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
// credits to Rich Brown
// source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
// specification: http://www.ietf.org/rfc/rfc4291.txt
URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
// gruber revised expression - http://rodneyrehm.de/t/url-regex.html
URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
// http://www.iana.org/assignments/uri-schemes.html
// http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
URI.defaultPorts = {
http: "80",
https: "443",
ftp: "21",
gopher: "70",
ws: "80",
wss: "443"
};
// allowed hostname characters according to RFC 3986
// ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded
// I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . -
URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/;
// map DOM Elements to their URI attribute
URI.domAttributes = {
'a': 'href',
'blockquote': 'cite',
'link': 'href',
'base': 'href',
'script': 'src',
'form': 'action',
'img': 'src',
'area': 'href',
'iframe': 'src',
'embed': 'src',
'source': 'src',
'track': 'src',
'input': 'src' // but only if type="image"
};
URI.getDomAttribute = function(node) {
if (!node || !node.nodeName) {
return undefined;
}
var nodeName = node.nodeName.toLowerCase();
// <input> should only expose src for type="image"
if (nodeName === 'input' && node.type !== 'image') {
return undefined;
}
return URI.domAttributes[nodeName];
};
function escapeForDumbFirefox36(value) {
// https://github.com/medialize/URI.js/issues/91
return escape(value);
}
// encoding / decoding according to RFC3986
function strictEncodeURIComponent(string) {
// see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent
return encodeURIComponent(string)
.replace(/[!'()*]/g, escapeForDumbFirefox36)
.replace(/\*/g, "%2A");
}
URI.encode = strictEncodeURIComponent;
URI.decode = decodeURIComponent;
URI.iso8859 = function() {
URI.encode = escape;
URI.decode = unescape;
};
URI.unicode = function() {
URI.encode = strictEncodeURIComponent;
URI.decode = decodeURIComponent;
};
URI.characters = {
pathname: {
encode: {
// RFC3986 2.1: For consistency, URI producers and normalizers should
// use uppercase hexadecimal digits for all percent-encodings.
expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig,
map: {
// -._~!'()*
"%24": "$",
"%26": "&",
"%2B": "+",
"%2C": ",",
"%3B": ";",
"%3D": "=",
"%3A": ":",
"%40": "@"
}
},
decode: {
expression: /[\/\?#]/g,
map: {
"/": "%2F",
"?": "%3F",
"#": "%23"
}
}
},
reserved: {
encode: {
// RFC3986 2.1: For consistency, URI producers and normalizers should
// use uppercase hexadecimal digits for all percent-encodings.
expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,
map: {
// gen-delims
"%3A": ":",
"%2F": "/",
"%3F": "?",
"%23": "#",
"%5B": "[",
"%5D": "]",
"%40": "@",
// sub-delims
"%21": "!",
"%24": "$",
"%26": "&",
"%27": "'",
"%28": "(",
"%29": ")",
"%2A": "*",
"%2B": "+",
"%2C": ",",
"%3B": ";",
"%3D": "="
}
}
}
};
URI.encodeQuery = function(string, escapeQuerySpace) {
var escaped = URI.encode(string + "");
return escapeQuerySpace ? escaped.replace(/%20/g, '+') : escaped;
};
URI.decodeQuery = function(string, escapeQuerySpace) {
string += "";
try {
return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
} catch(e) {
// we're not going to mess with weird encodings,
// give up and return the undecoded original string
// see https://github.com/medialize/URI.js/issues/87
// see https://github.com/medialize/URI.js/issues/92
return string;
}
};
URI.recodePath = function(string) {
var segments = (string + "").split('/');
for (var i = 0, length = segments.length; i < length; i++) {
segments[i] = URI.encodePathSegment(URI.decode(segments[i]));
}
return segments.join('/');
};
URI.decodePath = function(string) {
var segments = (string + "").split('/');
for (var i = 0, length = segments.length; i < length; i++) {
segments[i] = URI.decodePathSegment(segments[i]);
}
return segments.join('/');
};
// generate encode/decode path functions
var _parts = {'encode':'encode', 'decode':'decode'};
var _part;
var generateAccessor = function(_group, _part) {
return function(string) {
return URI[_part](string + "").replace(URI.characters[_group][_part].expression, function(c) {
return URI.characters[_group][_part].map[c];
});
};
};
for (_part in _parts) {
URI[_part + "PathSegment"] = generateAccessor("pathname", _parts[_part]);
}
URI.encodeReserved = generateAccessor("reserved", "encode");
URI.parse = function(string, parts) {
var pos;
if (!parts) {
parts = {};
}
// [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment]
// extract fragment
pos = string.indexOf('#');
if (pos > -1) {
// escaping?
parts.fragment = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract query
pos = string.indexOf('?');
if (pos > -1) {
// escaping?
parts.query = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract protocol
if (string.substring(0, 2) === '//') {
// relative-scheme
parts.protocol = null;
string = string.substring(2);
// extract "user:pass@host:port"
string = URI.parseAuthority(string, parts);
} else {
pos = string.indexOf(':');
if (pos > -1) {
parts.protocol = string.substring(0, pos) || null;
if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) {
// : may be within the path
parts.protocol = undefined;
} else if (parts.protocol === 'file') {
// the file scheme: does not contain an authority
string = string.substring(pos + 3);
} else if (string.substring(pos + 1, pos + 3) === '//') {
string = string.substring(pos + 3);
// extract "user:pass@host:port"
string = URI.parseAuthority(string, parts);
} else {
string = string.substring(pos + 1);
parts.urn = true;
}
}
}
// what's left must be the path
parts.path = string;
// and we're done
return parts;
};
URI.parseHost = function(string, parts) {
// extract host:port
var pos = string.indexOf('/');
var bracketPos;
var t;
if (pos === -1) {
pos = string.length;
}
if (string.charAt(0) === "[") {
// IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6
// I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
// IPv6+port in the format [2001:db8::1]:80 (for the time being)
bracketPos = string.indexOf(']');
parts.hostname = string.substring(1, bracketPos) || null;
parts.port = string.substring(bracketPos+2, pos) || null;
} else if (string.indexOf(':') !== string.lastIndexOf(':')) {
// IPv6 host contains multiple colons - but no port
// this notation is actually not allowed by RFC 3986, but we're a liberal parser
parts.hostname = string.substring(0, pos) || null;
parts.port = null;
} else {
t = string.substring(0, pos).split(':');
parts.hostname = t[0] || null;
parts.port = t[1] || null;
}
if (parts.hostname && string.substring(pos).charAt(0) !== '/') {
pos++;
string = "/" + string;
}
return string.substring(pos) || '/';
};
URI.parseAuthority = function(string, parts) {
string = URI.parseUserinfo(string, parts);
return URI.parseHost(string, parts);
};
URI.parseUserinfo = function(string, parts) {
// extract username:password
var firstSlash = string.indexOf('/');
var pos = firstSlash > -1
? string.lastIndexOf('@', firstSlash)
: string.indexOf('@');
var t;
// authority@ must come before /path
if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) {
t = string.substring(0, pos).split(':');
parts.username = t[0] ? URI.decode(t[0]) : null;
t.shift();
parts.password = t[0] ? URI.decode(t.join(':')) : null;
string = string.substring(pos + 1);
} else {
parts.username = null;
parts.password = null;
}
return string;
};
URI.parseQuery = function(string, escapeQuerySpace) {
if (!string) {
return {};
}
// throw out the funky business - "?"[name"="value"&"]+
string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
if (!string) {
return {};
}
var items = {};
var splits = string.split('&');
var length = splits.length;
var v, name, value;
for (var i = 0; i < length; i++) {
v = splits[i].split('=');
name = URI.decodeQuery(v.shift(), escapeQuerySpace);
// no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
if (items[name]) {
if (typeof items[name] === "string") {
items[name] = [items[name]];
}
items[name].push(value);
} else {
items[name] = value;
}
}
return items;
};
URI.build = function(parts) {
var t = "";
if (parts.protocol) {
t += parts.protocol + ":";
}
if (!parts.urn && (t || parts.hostname)) {
t += '//';
}
t += (URI.buildAuthority(parts) || '');
if (typeof parts.path === "string") {
if (parts.path.charAt(0) !== '/' && typeof parts.hostname === "string") {
t += '/';
}
t += parts.path;
}
if (typeof parts.query === "string" && parts.query) {
t += '?' + parts.query;
}
if (typeof parts.fragment === "string" && parts.fragment) {
t += '#' + parts.fragment;
}
return t;
};
URI.buildHost = function(parts) {
var t = "";
if (!parts.hostname) {
return "";
} else if (URI.ip6_expression.test(parts.hostname)) {
if (parts.port) {
t += "[" + parts.hostname + "]:" + parts.port;
} else {
// don't know if we should always wrap IPv6 in []
// the RFC explicitly says SHOULD, not MUST.
t += parts.hostname;
}
} else {
t += parts.hostname;
if (parts.port) {
t += ':' + parts.port;
}
}
return t;
};
URI.buildAuthority = function(parts) {
return URI.buildUserinfo(parts) + URI.buildHost(parts);
};
URI.buildUserinfo = function(parts) {
var t = "";
if (parts.username) {
t += URI.encode(parts.username);
if (parts.password) {
t += ':' + URI.encode(parts.password);
}
t += "@";
}
return t;
};
URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) {
// according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html
// being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed
// the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax!
// URI.js treats the query string as being application/x-www-form-urlencoded
// see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
var t = "";
var unique, key, i, length;
for (key in data) {
if (hasOwn.call(data, key) && key) {
if (isArray(data[key])) {
unique = {};
for (i = 0, length = data[key].length; i < length; i++) {
if (data[key][i] !== undefined && unique[data[key][i] + ""] === undefined) {
t += "&" + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace);
if (duplicateQueryParameters !== true) {
unique[data[key][i] + ""] = true;
}
}
}
} else if (data[key] !== undefined) {
t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace);
}
}
}
return t.substring(1);
};
URI.buildQueryParameter = function(name, value, escapeQuerySpace) {
// http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded
// don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization
return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? "=" + URI.encodeQuery(value, escapeQuerySpace) : "");
};
URI.addQuery = function(data, name, value) {
if (typeof name === "object") {
for (var key in name) {
if (hasOwn.call(name, key)) {
URI.addQuery(data, key, name[key]);
}
}
} else if (typeof name === "string") {
if (data[name] === undefined) {
data[name] = value;
return;
} else if (typeof data[name] === "string") {
data[name] = [data[name]];
}
if (!isArray(value)) {
value = [value];
}
data[name] = data[name].concat(value);
} else {
throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");
}
};
URI.removeQuery = function(data, name, value) {
var i, length, key;
if (isArray(name)) {
for (i = 0, length = name.length; i < length; i++) {
data[name[i]] = undefined;
}
} else if (typeof name === "object") {
for (key in name) {
if (hasOwn.call(name, key)) {
URI.removeQuery(data, key, name[key]);
}
}
} else if (typeof name === "string") {
if (value !== undefined) {
if (data[name] === value) {
data[name] = undefined;
} else if (isArray(data[name])) {
data[name] = filterArrayValues(data[name], value);
}
} else {
data[name] = undefined;
}
} else {
throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");
}
};
URI.hasQuery = function(data, name, value, withinArray) {
if (typeof name === "object") {
for (var key in name) {
if (hasOwn.call(name, key)) {
if (!URI.hasQuery(data, key, name[key])) {
return false;
}
}
}
return true;
} else if (typeof name !== "string") {
throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");
}
switch (getType(value)) {
case 'Undefined':
// true if exists (but may be empty)
return name in data; // data[name] !== undefined;
case 'Boolean':
// true if exists and non-empty
var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
return value === _booly;
case 'Function':
// allow complex comparison
return !!value(data[name], name, data);
case 'Array':
if (!isArray(data[name])) {
return false;
}
var op = withinArray ? arrayContains : arraysEqual;
return op(data[name], value);
case 'RegExp':
if (!isArray(data[name])) {
return Boolean(data[name] && data[name].match(value));
}
if (!withinArray) {
return false;
}
return arrayContains(data[name], value);
case 'Number':
value = String(value);
// omit break;
case 'String':
if (!isArray(data[name])) {
return data[name] === value;
}
if (!withinArray) {
return false;
}
return arrayContains(data[name], value);
default:
throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");
}
};
URI.commonPath = function(one, two) {
var length = Math.min(one.length, two.length);
var pos;
// find first non-matching character
for (pos = 0; pos < length; pos++) {
if (one.charAt(pos) !== two.charAt(pos)) {
pos--;
break;
}
}
if (pos < 1) {
return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : '';
}
// revert to last /
if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') {
pos = one.substring(0, pos).lastIndexOf('/');
}
return one.substring(0, pos + 1);
};
URI.withinString = function(string, callback) {
// expression used is "gruber revised" (@gruber v2) determined to be the best solution in
// a regex sprint we did a couple of ages ago at
// * http://mathiasbynens.be/demo/url-regex
// * http://rodneyrehm.de/t/url-regex.html
return string.replace(URI.find_uri_expression, callback);
};
URI.ensureValidHostname = function(v) {
// Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986)
// they are not part of DNS and therefore ignored by URI.js
if (v.match(URI.invalid_hostname_characters)) {
// test punycode
if (!punycode) {
throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-] and Punycode.js is not available");
}
if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) {
throw new TypeError("Hostname '" + v + "' contains characters other than [A-Z0-9.-]");
}
}
};
// noConflict
URI.noConflict = function(removeAll) {
if (removeAll) {
var unconflicted = {
URI: this.noConflict()
};
if (URITemplate && typeof URITemplate.noConflict == "function") {
unconflicted.URITemplate = URITemplate.noConflict();
}
if (IPv6 && typeof IPv6.noConflict == "function") {
unconflicted.IPv6 = IPv6.noConflict();
}
if (SecondLevelDomains && typeof SecondLevelDomains.noConflict == "function") {
unconflicted.SecondLevelDomains = SecondLevelDomains.noConflict();
}
return unconflicted;
} else if (root.URI === this) {
root.URI = _URI;
}
return this;
};
p.build = function(deferBuild) {
if (deferBuild === true) {
this._deferred_build = true;
} else if (deferBuild === undefined || this._deferred_build) {
this._string = URI.build(this._parts);
this._deferred_build = false;
}
return this;
};
p.clone = function() {
return new URI(this);
};
p.valueOf = p.toString = function() {
return this.build(false)._string;
};
// generate simple accessors
_parts = {protocol: 'protocol', username: 'username', password: 'password', hostname: 'hostname', port: 'port'};
generateAccessor = function(_part){
return function(v, build) {
if (v === undefined) {
return this._parts[_part] || "";
} else {
this._parts[_part] = v || null;
this.build(!build);
return this;
}
};
};
for (_part in _parts) {
p[_part] = generateAccessor(_parts[_part]);
}
// generate accessors with optionally prefixed input
_parts = {query: '?', fragment: '#'};
generateAccessor = function(_part, _key){
return function(v, build) {
if (v === undefined) {
return this._parts[_part] || "";
} else {
if (v !== null) {
v = v + "";
if (v.charAt(0) === _key) {
v = v.substring(1);
}
}
this._parts[_part] = v;
this.build(!build);
return this;
}
};
};
for (_part in _parts) {
p[_part] = generateAccessor(_part, _parts[_part]);
}
// generate accessors with prefixed output
_parts = {search: ['?', 'query'], hash: ['#', 'fragment']};
generateAccessor = function(_part, _key){
return function(v, build) {
var t = this[_part](v, build);
return typeof t === "string" && t.length ? (_key + t) : t;
};
};
for (_part in _parts) {
p[_part] = generateAccessor(_parts[_part][1], _parts[_part][0]);
}
p.pathname = function(v, build) {
if (v === undefined || v === true) {
var res = this._parts.path || (this._parts.hostname ? '/' : '');
return v ? URI.decodePath(res) : res;
} else {
this._parts.path = v ? URI.recodePath(v) : "/";
this.build(!build);
return this;
}
};
p.path = p.pathname;
p.href = function(href, build) {
var key;
if (href === undefined) {
return this.toString();
}
this._string = "";
this._parts = URI._parts();
var _URI = href instanceof URI;
var _object = typeof href === "object" && (href.hostname || href.path || href.pathname);
if (href.nodeName) {
var attribute = URI.getDomAttribute(href);
href = href[attribute] || "";
_object = false;
}
// window.location is reported to be an object, but it's not the sort
// of object we're looking for:
// * location.protocol ends with a colon
// * location.query != object.search
// * location.hash != object.fragment
// simply serializing the unknown object should do the trick
// (for location, not for everything...)
if (!_URI && _object && href.pathname !== undefined) {
href = href.toString();
}
if (typeof href === "string") {
this._parts = URI.parse(href, this._parts);
} else if (_URI || _object) {
var src = _URI ? href._parts : href;
for (key in src) {
if (hasOwn.call(this._parts, key)) {
this._parts[key] = src[key];
}
}
} else {
throw new TypeError("invalid input");
}
this.build(!build);
return this;
};
// identification accessors
p.is = function(what) {
var ip = false;
var ip4 = false;
var ip6 = false;
var name = false;
var sld = false;
var idn = false;
var punycode = false;
var relative = !this._parts.urn;
if (this._parts.hostname) {
relative = false;
ip4 = URI.ip4_expression.test(this._parts.hostname);
ip6 = URI.ip6_expression.test(this._parts.hostname);
ip = ip4 || ip6;
name = !ip;
sld = name && SLD && SLD.has(this._parts.hostname);
idn = name && URI.idn_expression.test(this._parts.hostname);
punycode = name && URI.punycode_expression.test(this._parts.hostname);
}
switch (what.toLowerCase()) {
case 'relative':
return relative;
case 'absolute':
return !relative;
// hostname identification
case 'domain':
case 'name':
return name;
case 'sld':
return sld;
case 'ip':
return ip;
case 'ip4':
case 'ipv4':
case 'inet4':
return ip4;
case 'ip6':
case 'ipv6':
case 'inet6':
return ip6;
case 'idn':
return idn;
case 'url':
return !this._parts.urn;
case 'urn':
return !!this._parts.urn;
case 'punycode':
return punycode;
}
return null;
};
// component specific input validation
var _protocol = p.protocol;
var _port = p.port;
var _hostname = p.hostname;
p.protocol = function(v, build) {
if (v !== undefined) {
if (v) {
// accept trailing ://
v = v.replace(/:(\/\/)?$/, '');
if (v.match(/[^a-zA-z0-9\.+-]/)) {
throw new TypeError("Protocol '" + v + "' contains characters other than [A-Z0-9.+-]");
}
}
}
return _protocol.call(this, v, build);
};
p.scheme = p.protocol;
p.port = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v !== undefined) {
if (v === 0) {
v = null;
}
if (v) {
v += "";
if (v.charAt(0) === ":") {
v = v.substring(1);
}
if (v.match(/[^0-9]/)) {
throw new TypeError("Port '" + v + "' contains characters other than [0-9]");
}
}
}
return _port.call(this, v, build);
};
p.hostname = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v !== undefined) {
var x = {};
URI.parseHost(v, x);
v = x.hostname;
}
return _hostname.call(this, v, build);
};
// compound accessors
p.host = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
return this._parts.hostname ? URI.buildHost(this._parts) : "";
} else {
URI.parseHost(v, this._parts);
this.build(!build);
return this;
}
};
p.authority = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
return this._parts.hostname ? URI.buildAuthority(this._parts) : "";
} else {
URI.parseAuthority(v, this._parts);
this.build(!build);
return this;
}
};
p.userinfo = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
if (!this._parts.username) {
return "";
}
var t = URI.buildUserinfo(this._parts);
return t.substring(0, t.length -1);
} else {
if (v[v.length-1] !== '@') {
v += '@';
}
URI.parseUserinfo(v, this._parts);
this.build(!build);
return this;
}
};
p.resource = function(v, build) {
var parts;
if (v === undefined) {
return this.path() + this.search() + this.hash();
}
parts = URI.parse(v);
this._parts.path = parts.path;
this._parts.query = parts.query;
this._parts.fragment = parts.fragment;
this.build(!build);
return this;
};
// fraction accessors
p.subdomain = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
// convenience, return "www" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return "";
}
// grab domain and add another segment
var end = this._parts.hostname.length - this.domain().length - 1;
return this._parts.hostname.substring(0, end) || "";
} else {
var e = this._parts.hostname.length - this.domain().length;
var sub = this._parts.hostname.substring(0, e);
var replace = new RegExp('^' + escapeRegEx(sub));
if (v && v.charAt(v.length - 1) !== '.') {
v += ".";
}
if (v) {
URI.ensureValidHostname(v);
}
this._parts.hostname = this._parts.hostname.replace(replace, v);
this.build(!build);
return this;
}
};
p.domain = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (typeof v === 'boolean') {
build = v;
v = undefined;
}
// convenience, return "example.org" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return "";
}
// if hostname consists of 1 or 2 segments, it must be the domain
var t = this._parts.hostname.match(/\./g);
if (t && t.length < 2) {
return this._parts.hostname;
}
// grab tld and add another segment
var end = this._parts.hostname.length - this.tld(build).length - 1;
end = this._parts.hostname.lastIndexOf('.', end -1) + 1;
return this._parts.hostname.substring(end) || "";
} else {
if (!v) {
throw new TypeError("cannot set domain empty");
}
URI.ensureValidHostname(v);
if (!this._parts.hostname || this.is('IP')) {
this._parts.hostname = v;
} else {
var replace = new RegExp(escapeRegEx(this.domain()) + "$");
this._parts.hostname = this._parts.hostname.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.tld = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (typeof v === 'boolean') {
build = v;
v = undefined;
}
// return "org" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return "";
}
var pos = this._parts.hostname.lastIndexOf('.');
var tld = this._parts.hostname.substring(pos + 1);
if (build !== true && SLD && SLD.list[tld.toLowerCase()]) {
return SLD.get(this._parts.hostname) || tld;
}
return tld;
} else {
var replace;
if (!v) {
throw new TypeError("cannot set TLD empty");
} else if (v.match(/[^a-zA-Z0-9-]/)) {
if (SLD && SLD.is(v)) {
replace = new RegExp(escapeRegEx(this.tld()) + "$");
this._parts.hostname = this._parts.hostname.replace(replace, v);
} else {
throw new TypeError("TLD '" + v + "' contains characters other than [A-Z0-9]");
}
} else if (!this._parts.hostname || this.is('IP')) {
throw new ReferenceError("cannot set TLD on non-domain host");
} else {
replace = new RegExp(escapeRegEx(this.tld()) + "$");
this._parts.hostname = this._parts.hostname.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.directory = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path && !this._parts.hostname) {
return '';
}
if (this._parts.path === '/') {
return '/';
}
var end = this._parts.path.length - this.filename().length - 1;
var res = this._parts.path.substring(0, end) || (this._parts.hostname ? "/" : "");
return v ? URI.decodePath(res) : res;
} else {
var e = this._parts.path.length - this.filename().length;
var directory = this._parts.path.substring(0, e);
var replace = new RegExp('^' + escapeRegEx(directory));
// fully qualifier directories begin with a slash
if (!this.is('relative')) {
if (!v) {
v = '/';
}
if (v.charAt(0) !== '/') {
v = "/" + v;
}
}
// directories always end with a slash
if (v && v.charAt(v.length - 1) !== '/') {
v += '/';
}
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
this.build(!build);
return this;
}
};
p.filename = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path || this._parts.path === '/') {
return "";
}
var pos = this._parts.path.lastIndexOf('/');
var res = this._parts.path.substring(pos+1);
return v ? URI.decodePathSegment(res) : res;
} else {
var mutatedDirectory = false;
if (v.charAt(0) === '/') {
v = v.substring(1);
}
if (v.match(/\.?\//)) {
mutatedDirectory = true;
}
var replace = new RegExp(escapeRegEx(this.filename()) + "$");
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
if (mutatedDirectory) {
this.normalizePath(build);
} else {
this.build(!build);
}
return this;
}
};
p.suffix = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path || this._parts.path === '/') {
return "";
}
var filename = this.filename();
var pos = filename.lastIndexOf('.');
var s, res;
if (pos === -1) {
return "";
}
// suffix may only contain alnum characters (yup, I made this up.)
s = filename.substring(pos+1);
res = (/^[a-z0-9%]+$/i).test(s) ? s : "";
return v ? URI.decodePathSegment(res) : res;
} else {
if (v.charAt(0) === '.') {
v = v.substring(1);
}
var suffix = this.suffix();
var replace;
if (!suffix) {
if (!v) {
return this;
}
this._parts.path += '.' + URI.recodePath(v);
} else if (!v) {
replace = new RegExp(escapeRegEx("." + suffix) + "$");
} else {
replace = new RegExp(escapeRegEx(suffix) + "$");
}
if (replace) {
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.segment = function(segment, v, build) {
var separator = this._parts.urn ? ':' : '/';
var path = this.path();
var absolute = path.substring(0, 1) === '/';
var segments = path.split(separator);
if (segment !== undefined && typeof segment !== 'number') {
build = v;
v = segment;
segment = undefined;
}
if (segment !== undefined && typeof segment !== 'number') {
throw new Error("Bad segment '" + segment + "', must be 0-based integer");
}
if (absolute) {
segments.shift();
}
if (segment < 0) {
// allow negative indexes to address from the end
segment = Math.max(segments.length + segment, 0);
}
if (v === undefined) {
return segment === undefined
? segments
: segments[segment];
} else if (segment === null || segments[segment] === undefined) {
if (isArray(v)) {
segments = [];
// collapse empty elements within array
for (var i=0, l=v.length; i < l; i++) {
if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) {
continue;
}
if (segments.length && !segments[segments.length -1].length) {
segments.pop();
}
segments.push(v[i]);
}
} else if (v || (typeof v === "string")) {
if (segments[segments.length -1] === "") {
// empty trailing elements have to be overwritten
// to prevent results such as /foo//bar
segments[segments.length -1] = v;
} else {
segments.push(v);
}
}
} else {
if (v || (typeof v === "string" && v.length)) {
segments[segment] = v;
} else {
segments.splice(segment, 1);
}
}
if (absolute) {
segments.unshift("");
}
return this.path(segments.join(separator), build);
};
p.segmentCoded = function(segment, v, build) {
var segments, i, l;
if (typeof segment !== 'number') {
build = v;
v = segment;
segment = undefined;
}
if (v === undefined) {
segments = this.segment(segment, v, build);
if (!isArray(segments)) {
segments = segments !== undefined ? URI.decode(segments) : undefined;
} else {
for (i = 0, l = segments.length; i < l; i++) {
segments[i] = URI.decode(segments[i]);
}
}
return segments;
}
if (!isArray(v)) {
v = typeof v === 'string' ? URI.encode(v) : v;
} else {
for (i = 0, l = v.length; i < l; i++) {
v[i] = URI.decode(v[i]);
}
}
return this.segment(segment, v, build);
};
// mutating query string
var q = p.query;
p.query = function(v, build) {
if (v === true) {
return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
} else if (typeof v === "function") {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
var result = v.call(this, data);
this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
this.build(!build);
return this;
} else if (v !== undefined && typeof v !== "string") {
this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
this.build(!build);
return this;
} else {
return q.call(this, v, build);
}
};
p.setQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
if (typeof name === "object") {
for (var key in name) {
if (hasOwn.call(name, key)) {
data[key] = name[key];
}
}
} else if (typeof name === "string") {
data[name] = value !== undefined ? value : null;
} else {
throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");
}
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== "string") {
build = value;
}
this.build(!build);
return this;
};
p.addQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
URI.addQuery(data, name, value === undefined ? null : value);
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== "string") {
build = value;
}
this.build(!build);
return this;
};
p.removeQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
URI.removeQuery(data, name, value);
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== "string") {
build = value;
}
this.build(!build);
return this;
};
p.hasQuery = function(name, value, withinArray) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
return URI.hasQuery(data, name, value, withinArray);
};
p.setSearch = p.setQuery;
p.addSearch = p.addQuery;
p.removeSearch = p.removeQuery;
p.hasSearch = p.hasQuery;
// sanitizing URLs
p.normalize = function() {
if (this._parts.urn) {
return this
.normalizeProtocol(false)
.normalizeQuery(false)
.normalizeFragment(false)
.build();
}
return this
.normalizeProtocol(false)
.normalizeHostname(false)
.normalizePort(false)
.normalizePath(false)
.normalizeQuery(false)
.normalizeFragment(false)
.build();
};
p.normalizeProtocol = function(build) {
if (typeof this._parts.protocol === "string") {
this._parts.protocol = this._parts.protocol.toLowerCase();
this.build(!build);
}
return this;
};
p.normalizeHostname = function(build) {
if (this._parts.hostname) {
if (this.is('IDN') && punycode) {
this._parts.hostname = punycode.toASCII(this._parts.hostname);
} else if (this.is('IPv6') && IPv6) {
this._parts.hostname = IPv6.best(this._parts.hostname);
}
this._parts.hostname = this._parts.hostname.toLowerCase();
this.build(!build);
}
return this;
};
p.normalizePort = function(build) {
// remove port of it's the protocol's default
if (typeof this._parts.protocol === "string" && this._parts.port === URI.defaultPorts[this._parts.protocol]) {
this._parts.port = null;
this.build(!build);
}
return this;
};
p.normalizePath = function(build) {
if (this._parts.urn) {
return this;
}
if (!this._parts.path || this._parts.path === '/') {
return this;
}
var _was_relative;
var _path = this._parts.path;
var _parent, _pos;
// handle relative paths
if (_path.charAt(0) !== '/') {
_was_relative = true;
_path = '/' + _path;
}
// resolve simples
_path = _path
.replace(/(\/(\.\/)+)|(\/\.$)/g, '/')
.replace(/\/{2,}/g, '/');
// resolve parents
while (true) {
_parent = _path.indexOf('/../');
if (_parent === -1) {
// no more ../ to resolve
break;
} else if (_parent === 0) {
// top level cannot be relative...
_path = _path.substring(3);
break;
}
_pos = _path.substring(0, _parent).lastIndexOf('/');
if (_pos === -1) {
_pos = _parent;
}
_path = _path.substring(0, _pos) + _path.substring(_parent + 3);
}
// revert to relative
if (_was_relative && this.is('relative')) {
_path = _path.substring(1);
}
_path = URI.recodePath(_path);
this._parts.path = _path;
this.build(!build);
return this;
};
p.normalizePathname = p.normalizePath;
p.normalizeQuery = function(build) {
if (typeof this._parts.query === "string") {
if (!this._parts.query.length) {
this._parts.query = null;
} else {
this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace));
}
this.build(!build);
}
return this;
};
p.normalizeFragment = function(build) {
if (!this._parts.fragment) {
this._parts.fragment = null;
this.build(!build);
}
return this;
};
p.normalizeSearch = p.normalizeQuery;
p.normalizeHash = p.normalizeFragment;
p.iso8859 = function() {
// expect unicode input, iso8859 output
var e = URI.encode;
var d = URI.decode;
URI.encode = escape;
URI.decode = decodeURIComponent;
this.normalize();
URI.encode = e;
URI.decode = d;
return this;
};
p.unicode = function() {
// expect iso8859 input, unicode output
var e = URI.encode;
var d = URI.decode;
URI.encode = strictEncodeURIComponent;
URI.decode = unescape;
this.normalize();
URI.encode = e;
URI.decode = d;
return this;
};
p.readable = function() {
var uri = this.clone();
// removing username, password, because they shouldn't be displayed according to RFC 3986
uri.username("").password("").normalize();
var t = '';
if (uri._parts.protocol) {
t += uri._parts.protocol + '://';
}
if (uri._parts.hostname) {
if (uri.is('punycode') && punycode) {
t += punycode.toUnicode(uri._parts.hostname);
if (uri._parts.port) {
t += ":" + uri._parts.port;
}
} else {
t += uri.host();
}
}
if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') {
t += '/';
}
t += uri.path(true);
if (uri._parts.query) {
var q = '';
for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) {
var kv = (qp[i] || "").split('=');
q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace)
.replace(/&/g, '%26');
if (kv[1] !== undefined) {
q += "=" + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace)
.replace(/&/g, '%26');
}
}
t += '?' + q.substring(1);
}
t += URI.decodeQuery(uri.hash(), true);
return t;
};
// resolving relative and absolute URLs
p.absoluteTo = function(base) {
var resolved = this.clone();
var properties = ['protocol', 'username', 'password', 'hostname', 'port'];
var basedir, i, p;
if (this._parts.urn) {
throw new Error('URNs do not have any generally defined hierarchical components');
}
if (!(base instanceof URI)) {
base = new URI(base);
}
if (!resolved._parts.protocol) {
resolved._parts.protocol = base._parts.protocol;
}
if (this._parts.hostname) {
return resolved;
}
for (i = 0; p = properties[i]; i++) {
resolved._parts[p] = base._parts[p];
}
properties = ['query', 'path'];
for (i = 0; p = properties[i]; i++) {
if (!resolved._parts[p] && base._parts[p]) {
resolved._parts[p] = base._parts[p];
}
}
if (resolved.path().charAt(0) !== '/') {
basedir = base.directory();
resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path;
resolved.normalizePath();
}
resolved.build();
return resolved;
};
p.relativeTo = function(base) {
var relative = this.clone().normalize();
var relativeParts, baseParts, common, relativePath, basePath;
if (relative._parts.urn) {
throw new Error('URNs do not have any generally defined hierarchical components');
}
base = new URI(base).normalize();
relativeParts = relative._parts;
baseParts = base._parts;
relativePath = relative.path();
basePath = base.path();
if (relativePath.charAt(0) !== '/') {
throw new Error('URI is already relative');
}
if (basePath.charAt(0) !== '/') {
throw new Error('Cannot calculate a URI relative to another relative URI');
}
if (relativeParts.protocol === baseParts.protocol) {
relativeParts.protocol = null;
}
if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) {
return relative.build();
}
if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) {
return relative.build();
}
if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) {
relativeParts.hostname = null;
relativeParts.port = null;
} else {
return relative.build();
}
if (relativePath === basePath) {
relativeParts.path = '';
return relative.build();
}
// determine common sub path
common = URI.commonPath(relative.path(), base.path());
// If the paths have nothing in common, return a relative URL with the absolute path.
if (!common) {
return relative.build();
}
var parents = baseParts.path
.substring(common.length)
.replace(/[^\/]*$/, '')
.replace(/.*?\//g, '../');
relativeParts.path = parents + relativeParts.path.substring(common.length);
return relative.build();
};
// comparing URIs
p.equals = function(uri) {
var one = this.clone();
var two = new URI(uri);
var one_map = {};
var two_map = {};
var checked = {};
var one_query, two_query, key;
one.normalize();
two.normalize();
// exact match
if (one.toString() === two.toString()) {
return true;
}
// extract query string
one_query = one.query();
two_query = two.query();
one.query("");
two.query("");
// definitely not equal if not even non-query parts match
if (one.toString() !== two.toString()) {
return false;
}
// query parameters have the same length, even if they're permuted
if (one_query.length !== two_query.length) {
return false;
}
one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace);
two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace);
for (key in one_map) {
if (hasOwn.call(one_map, key)) {
if (!isArray(one_map[key])) {
if (one_map[key] !== two_map[key]) {
return false;
}
} else if (!arraysEqual(one_map[key], two_map[key])) {
return false;
}
checked[key] = true;
}
}
for (key in two_map) {
if (hasOwn.call(two_map, key)) {
if (!checked[key]) {
// two contains a parameter not present in one
return false;
}
}
}
return true;
};
// state
p.duplicateQueryParameters = function(v) {
this._parts.duplicateQueryParameters = !!v;
return this;
};
p.escapeQuerySpace = function(v) {
this._parts.escapeQuerySpace = !!v;
return this;
};
return URI;
}));
/*!
* jQuery UI Position 1.10.4
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/position/
*/
(function( $, undefined ) {
$.ui = $.ui || {};
var cachedScrollbarWidth,
max = Math.max,
abs = Math.abs,
round = Math.round,
rhorizontal = /left|center|right/,
rvertical = /top|center|bottom/,
roffset = /[\+\-]\d+(\.[\d]+)?%?/,
rposition = /^\w+/,
rpercent = /%$/,
_position = $.fn.position;
function getOffsets( offsets, width, height ) {
return [
parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
];
}
function parseCss( element, property ) {
return parseInt( $.css( element, property ), 10 ) || 0;
}
function getDimensions( elem ) {
var raw = elem[0];
if ( raw.nodeType === 9 ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: 0, left: 0 }
};
}
if ( $.isWindow( raw ) ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
};
}
if ( raw.preventDefault ) {
return {
width: 0,
height: 0,
offset: { top: raw.pageY, left: raw.pageX }
};
}
return {
width: elem.outerWidth(),
height: elem.outerHeight(),
offset: elem.offset()
};
}
$.position = {
scrollbarWidth: function() {
if ( cachedScrollbarWidth !== undefined ) {
return cachedScrollbarWidth;
}
var w1, w2,
div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
innerDiv = div.children()[0];
$( "body" ).append( div );
w1 = innerDiv.offsetWidth;
div.css( "overflow", "scroll" );
w2 = innerDiv.offsetWidth;
if ( w1 === w2 ) {
w2 = div[0].clientWidth;
}
div.remove();
return (cachedScrollbarWidth = w1 - w2);
},
getScrollInfo: function( within ) {
var overflowX = within.isWindow || within.isDocument ? "" :
within.element.css( "overflow-x" ),
overflowY = within.isWindow || within.isDocument ? "" :
within.element.css( "overflow-y" ),
hasOverflowX = overflowX === "scroll" ||
( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
hasOverflowY = overflowY === "scroll" ||
( overflowY === "auto" && within.height < within.element[0].scrollHeight );
return {
width: hasOverflowY ? $.position.scrollbarWidth() : 0,
height: hasOverflowX ? $.position.scrollbarWidth() : 0
};
},
getWithinInfo: function( element ) {
var withinElement = $( element || window ),
isWindow = $.isWindow( withinElement[0] ),
isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
return {
element: withinElement,
isWindow: isWindow,
isDocument: isDocument,
offset: withinElement.offset() || { left: 0, top: 0 },
scrollLeft: withinElement.scrollLeft(),
scrollTop: withinElement.scrollTop(),
width: isWindow ? withinElement.width() : withinElement.outerWidth(),
height: isWindow ? withinElement.height() : withinElement.outerHeight()
};
}
};
$.fn.position = function( options ) {
if ( !options || !options.of ) {
return _position.apply( this, arguments );
}
// make a copy, we don't want to modify arguments
options = $.extend( {}, options );
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
target = $( options.of ),
within = $.position.getWithinInfo( options.within ),
scrollInfo = $.position.getScrollInfo( within ),
collision = ( options.collision || "flip" ).split( " " ),
offsets = {};
dimensions = getDimensions( target );
if ( target[0].preventDefault ) {
// force left top to allow flipping
options.at = "left top";
}
targetWidth = dimensions.width;
targetHeight = dimensions.height;
targetOffset = dimensions.offset;
// clone to reuse original targetOffset later
basePosition = $.extend( {}, targetOffset );
// force my and at to have valid horizontal and vertical positions
// if a value is missing or invalid, it will be converted to center
$.each( [ "my", "at" ], function() {
var pos = ( options[ this ] || "" ).split( " " ),
horizontalOffset,
verticalOffset;
if ( pos.length === 1) {
pos = rhorizontal.test( pos[ 0 ] ) ?
pos.concat( [ "center" ] ) :
rvertical.test( pos[ 0 ] ) ?
[ "center" ].concat( pos ) :
[ "center", "center" ];
}
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
// calculate offsets
horizontalOffset = roffset.exec( pos[ 0 ] );
verticalOffset = roffset.exec( pos[ 1 ] );
offsets[ this ] = [
horizontalOffset ? horizontalOffset[ 0 ] : 0,
verticalOffset ? verticalOffset[ 0 ] : 0
];
// reduce to just the positions without the offsets
options[ this ] = [
rposition.exec( pos[ 0 ] )[ 0 ],
rposition.exec( pos[ 1 ] )[ 0 ]
];
});
// normalize collision option
if ( collision.length === 1 ) {
collision[ 1 ] = collision[ 0 ];
}
if ( options.at[ 0 ] === "right" ) {
basePosition.left += targetWidth;
} else if ( options.at[ 0 ] === "center" ) {
basePosition.left += targetWidth / 2;
}
if ( options.at[ 1 ] === "bottom" ) {
basePosition.top += targetHeight;
} else if ( options.at[ 1 ] === "center" ) {
basePosition.top += targetHeight / 2;
}
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
basePosition.left += atOffset[ 0 ];
basePosition.top += atOffset[ 1 ];
return this.each(function() {
var collisionPosition, using,
elem = $( this ),
elemWidth = elem.outerWidth(),
elemHeight = elem.outerHeight(),
marginLeft = parseCss( this, "marginLeft" ),
marginTop = parseCss( this, "marginTop" ),
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
position = $.extend( {}, basePosition ),
myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
if ( options.my[ 0 ] === "right" ) {
position.left -= elemWidth;
} else if ( options.my[ 0 ] === "center" ) {
position.left -= elemWidth / 2;
}
if ( options.my[ 1 ] === "bottom" ) {
position.top -= elemHeight;
} else if ( options.my[ 1 ] === "center" ) {
position.top -= elemHeight / 2;
}
position.left += myOffset[ 0 ];
position.top += myOffset[ 1 ];
// if the browser doesn't support fractions, then round for consistent results
if ( !$.support.offsetFractions ) {
position.left = round( position.left );
position.top = round( position.top );
}
collisionPosition = {
marginLeft: marginLeft,
marginTop: marginTop
};
$.each( [ "left", "top" ], function( i, dir ) {
if ( $.ui.position[ collision[ i ] ] ) {
$.ui.position[ collision[ i ] ][ dir ]( position, {
targetWidth: targetWidth,
targetHeight: targetHeight,
elemWidth: elemWidth,
elemHeight: elemHeight,
collisionPosition: collisionPosition,
collisionWidth: collisionWidth,
collisionHeight: collisionHeight,
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
my: options.my,
at: options.at,
within: within,
elem : elem
});
}
});
if ( options.using ) {
// adds feedback as second argument to using callback, if present
using = function( props ) {
var left = targetOffset.left - position.left,
right = left + targetWidth - elemWidth,
top = targetOffset.top - position.top,
bottom = top + targetHeight - elemHeight,
feedback = {
target: {
element: target,
left: targetOffset.left,
top: targetOffset.top,
width: targetWidth,
height: targetHeight
},
element: {
element: elem,
left: position.left,
top: position.top,
width: elemWidth,
height: elemHeight
},
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
};
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
feedback.horizontal = "center";
}
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
feedback.vertical = "middle";
}
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
feedback.important = "horizontal";
} else {
feedback.important = "vertical";
}
options.using.call( this, props, feedback );
};
}
elem.offset( $.extend( position, { using: using } ) );
});
};
$.ui.position = {
fit: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
outerWidth = within.width,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = withinOffset - collisionPosLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
newOverRight;
// element is wider than within
if ( data.collisionWidth > outerWidth ) {
// element is initially over the left side of within
if ( overLeft > 0 && overRight <= 0 ) {
newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
position.left += overLeft - newOverRight;
// element is initially over right side of within
} else if ( overRight > 0 && overLeft <= 0 ) {
position.left = withinOffset;
// element is initially over both left and right sides of within
} else {
if ( overLeft > overRight ) {
position.left = withinOffset + outerWidth - data.collisionWidth;
} else {
position.left = withinOffset;
}
}
// too far left -> align with left edge
} else if ( overLeft > 0 ) {
position.left += overLeft;
// too far right -> align with right edge
} else if ( overRight > 0 ) {
position.left -= overRight;
// adjust based on position and margin
} else {
position.left = max( position.left - collisionPosLeft, position.left );
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
outerHeight = data.within.height,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = withinOffset - collisionPosTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
newOverBottom;
// element is taller than within
if ( data.collisionHeight > outerHeight ) {
// element is initially over the top of within
if ( overTop > 0 && overBottom <= 0 ) {
newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
position.top += overTop - newOverBottom;
// element is initially over bottom of within
} else if ( overBottom > 0 && overTop <= 0 ) {
position.top = withinOffset;
// element is initially over both top and bottom of within
} else {
if ( overTop > overBottom ) {
position.top = withinOffset + outerHeight - data.collisionHeight;
} else {
position.top = withinOffset;
}
}
// too far up -> align with top
} else if ( overTop > 0 ) {
position.top += overTop;
// too far down -> align with bottom edge
} else if ( overBottom > 0 ) {
position.top -= overBottom;
// adjust based on position and margin
} else {
position.top = max( position.top - collisionPosTop, position.top );
}
}
},
flip: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.offset.left + within.scrollLeft,
outerWidth = within.width,
offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = collisionPosLeft - offsetLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
myOffset = data.my[ 0 ] === "left" ?
-data.elemWidth :
data.my[ 0 ] === "right" ?
data.elemWidth :
0,
atOffset = data.at[ 0 ] === "left" ?
data.targetWidth :
data.at[ 0 ] === "right" ?
-data.targetWidth :
0,
offset = -2 * data.offset[ 0 ],
newOverRight,
newOverLeft;
if ( overLeft < 0 ) {
newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
position.left += myOffset + atOffset + offset;
}
}
else if ( overRight > 0 ) {
newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
position.left += myOffset + atOffset + offset;
}
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.offset.top + within.scrollTop,
outerHeight = within.height,
offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = collisionPosTop - offsetTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
top = data.my[ 1 ] === "top",
myOffset = top ?
-data.elemHeight :
data.my[ 1 ] === "bottom" ?
data.elemHeight :
0,
atOffset = data.at[ 1 ] === "top" ?
data.targetHeight :
data.at[ 1 ] === "bottom" ?
-data.targetHeight :
0,
offset = -2 * data.offset[ 1 ],
newOverTop,
newOverBottom;
if ( overTop < 0 ) {
newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
position.top += myOffset + atOffset + offset;
}
}
else if ( overBottom > 0 ) {
newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
position.top += myOffset + atOffset + offset;
}
}
}
},
flipfit: {
left: function() {
$.ui.position.flip.left.apply( this, arguments );
$.ui.position.fit.left.apply( this, arguments );
},
top: function() {
$.ui.position.flip.top.apply( this, arguments );
$.ui.position.fit.top.apply( this, arguments );
}
}
};
// fraction support test
(function () {
var testElement, testElementParent, testElementStyle, offsetLeft, i,
body = document.getElementsByTagName( "body" )[ 0 ],
div = document.createElement( "div" );
//Create a "fake body" for testing based on method used in jQuery.support
testElement = document.createElement( body ? "div" : "body" );
testElementStyle = {
visibility: "hidden",
width: 0,
height: 0,
border: 0,
margin: 0,
background: "none"
};
if ( body ) {
$.extend( testElementStyle, {
position: "absolute",
left: "-1000px",
top: "-1000px"
});
}
for ( i in testElementStyle ) {
testElement.style[ i ] = testElementStyle[ i ];
}
testElement.appendChild( div );
testElementParent = body || document.documentElement;
testElementParent.insertBefore( testElement, testElementParent.firstChild );
div.style.cssText = "position: absolute; left: 10.7432222px;";
offsetLeft = $( div ).offset().left;
$.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
testElement.innerHTML = "";
testElementParent.removeChild( testElement );
})();
}( jQuery ) );
/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides ECMA Script 6 Polyfill
(function(jQuery) {
"use strict";
/*
* No Documentation by intention.
* This class represents a polyfill for ECMA Script 6 Promises
* see http://www.html5rocks.com/en/tutorials/es6/promises/
*/
var Promise = function(fAction) {
if (typeof (fAction) != "function") {
throw new TypeError("Argument is not a function");
}
this._deferred = new jQuery.Deferred();
try {
var that = this;
fAction(function(oVal){
_finalize(that, oVal, true); //force async resolve
}, function(oVal){
_finalize(that, oVal, false); //force async reject
});
} catch (e) { //Error in action rejects the promise
_finalize(this, e, false);
}
};
// *** Instance Promise functions ***
Promise.prototype.then = function(fOnFulfilled, fOnRejected){
var oFollowUpPromise = new Promise(_dummy);
this._deferred.then(_doWrap(fOnFulfilled, oFollowUpPromise, true), _doWrap(fOnRejected, oFollowUpPromise, false));
return oFollowUpPromise;
};
Promise.prototype["catch"] = function(fOnRejected){
return this.then(undefined, fOnRejected);
};
// *** Static Promise functions ***
Promise.all = function(aPromises){
return new Promise(function(fResolve, fReject){
if (!jQuery.isArray(aPromises)) {
fReject(new TypeError("invalid argument"));
return;
}
if (aPromises.length == 0) {
fResolve([]);
return;
}
var bFailed = false,
aValues = new Array(aPromises.length),
iCount = 0;
function _check(iIdx){
Promise.resolve(aPromises[iIdx]).then(function(oObj){
if (!bFailed) {
iCount++;
aValues[iIdx] = oObj;
if (iCount == aPromises.length) {
fResolve(aValues);
}
}
}, function(oObj){
if (!bFailed) {
bFailed = true;
fReject(oObj);
}
});
}
for (var i = 0; i < aPromises.length; i++) {
_check(i);
}
});
};
Promise.race = function(aPromises){
return new Promise(function(fResolve, fReject){
if (!jQuery.isArray(aPromises)) {
fReject(new TypeError("invalid argument"));
}
var bFinal = false;
for (var i = 0; i < aPromises.length; i++) {
/*eslint-disable no-loop-func */
Promise.resolve(aPromises[i]).then(function(oObj){
if (!bFinal) {
bFinal = true;
fResolve(oObj);
}
}, function(oObj){
if (!bFinal) {
bFinal = true;
fReject(oObj);
}
});
/*eslint-enable no-loop-func */
}
});
};
Promise.resolve = function(oObj){
return oObj instanceof Promise ? oObj : _resolve(new Promise(_dummy), oObj);
};
Promise.reject = function(oObj){
return _finalize(new Promise(_dummy), oObj, false);
};
// *** Helper functions ***
function _dummy(){}
function _isThenable(oObj){
return oObj && oObj.then && typeof (oObj.then) == "function";
}
function _finalize(oPromise, oObj, bResolve){
setTimeout(function(){
if (_isThenable(oObj) && bResolve) { //Assimilation
_resolve(oPromise, oObj);
} else {
oPromise._deferred[bResolve ? "resolve" : "reject"](oObj);
}
}, 0);
return oPromise;
}
function _resolve(oPromise, oObj){
if (_isThenable(oObj)) {
var bFinal = false;
try {
oObj.then(function(oVal){
_finalize(oPromise, oVal, true);
bFinal = true;
}, function(oVal){
_finalize(oPromise, oVal, false);
bFinal = true;
});
} catch (e) {
if (!bFinal) {
_finalize(oPromise, e, false);
} else {
jQuery.sap.log.debug("Promise: Error in then: " + e); //Error is ignored
}
}
} else {
_finalize(oPromise, oObj, true);
}
return oPromise;
}
function _doWrap(fAction, oPromise, bResolve){
return function(oObj){
if (!fAction) {
_finalize(oPromise, oObj, bResolve);
} else {
try {
_resolve(oPromise, fAction(oObj));
} catch (e) { //catch error in fAction
_finalize(oPromise, e, false);
}
}
};
}
// *** Polyfill ***
if (!window.Promise) {
window.Promise = Promise;
}
if (window.sap && window.sap.__ui5PublishPromisePolyfill) { //For testing purposes
window._UI5Promise = Promise;
}
})(jQuery);
/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*global URI, Promise, alert, console, XMLHttpRequest */
/**
* @class Provides base functionality of the SAP jQuery plugin as extension of the jQuery framework.<br/>
* See also <a href="http://api.jquery.com/jQuery/">jQuery</a> for details.<br/>
* Although these functions appear as static ones, they are meant to be used on jQuery instances.<br/>
* If not stated differently, the functions follow the fluent interface paradigm and return the jQuery instance for chaining of statements.
*
* Example for usage of an instance method:
* <pre>
* var oRect = jQuery("#myDiv").rect();
* alert("Top Position: " + oRect.top);
* </pre>
*
* @name jQuery
* @static
* @public
*/
(function() {
if (!window.jQuery ) {
throw new Error("SAPUI5 requires jQuery as a prerequisite (>= version 1.7)");
}
// ensure not to initialize twice
if (jQuery.sap) {
return;
}
/**
* Window that the sap plugin has been initialized for.
* @private
*/
var _window = window;
// early logging support
var _earlyLogs = [];
function _earlyLog(sLevel, sMessage) {
_earlyLogs.push({
level: sLevel,
message: sMessage
});
}
var _sBootstrapUrl;
// -------------------------- VERSION -------------------------------------
var rVersion = /^[0-9]+(?:\.([0-9]+)(?:\.([0-9]+))?)?(.*)$/;
/**
* Returns a Version instance created from the given parameters.
*
* This function can either be called as a constructor (using <code>new</code>) or as a normal function.
* It always returns an immutable Version instance.
*
* The parts of the version number (major, minor, patch, suffix) can be provided in several ways:
* <ul>
* <li>Version("1.2.3-SNAPSHOT") - as a dot-separated string. Any non-numerical char or a dot followed by a non-numerical char starts the suffix portion.
* Any missing major, minor or patch versions will be set to 0.</li>
* <li>Version(1,2,3,"-SNAPSHOT") - as individual parameters. Major, minor and patch must be integer numbers or empty, suffix must be a string not starting with digits.</li>
* <li>Version([1,2,3,"-SNAPSHOT"]) - as an array with the individual parts. The same type restrictions apply as before.</li>
* <li>Version(otherVersion) - as a Version instance (cast operation). Returns the given instance instead of creating a new one.</li>
* </ul>
*
* To keep the code size small, this implementation mainly validates the single string variant.
* All other variants are only validated to some degree. It is the responsibility of the caller to
* provide proper parts.
*
* @param {int|string|any[]|jQuery.sap.Version} vMajor the major part of the version (int) or any of the single parameter variants explained above.
* @param {int} iMinor the minor part of the version number
* @param {int} iPatch the patch part of the version number
* @param {string} sSuffix the suffix part of the version number
* @return {jQuery.sap.Version} the version object as determined from the parameters
*
* @class Represents a version consisting of major, minor, patch version and suffix, e.g. '1.2.7-SNAPSHOT'.
*
* @author SAP SE
* @version 1.28.5
* @constructor
* @public
* @since 1.15.0
* @name jQuery.sap.Version
*/
function Version(vMajor, iMinor, iPatch, sSuffix) {
if ( vMajor instanceof Version ) {
// note: even a constructor may return a value different from 'this'
return vMajor;
}
if ( !(this instanceof Version) ) {
// act as a cast operator when called as function (not as a constructor)
return new Version(vMajor, iMinor, iPatch, sSuffix);
}
var m;
if (typeof vMajor === "string") {
m = rVersion.exec(vMajor);
} else if (jQuery.isArray(vMajor)) {
m = vMajor;
} else {
m = arguments;
}
m = m || [];
function norm(v) {
v = parseInt(v,10);
return isNaN(v) ? 0 : v;
}
vMajor = norm(m[0]);
iMinor = norm(m[1]);
iPatch = norm(m[2]);
sSuffix = String(m[3] || "");
/**
* Returns a string representation of this version.
*
* @return {string} a string representation of this version.
* @name jQuery.sap.Version#toString
* @public
* @since 1.15.0
* @function
*/
this.toString = function() {
return vMajor + "." + iMinor + "." + iPatch + sSuffix;
};
/**
* Returns the major version part of this version.
*
* @return {int} the major version part of this version
* @name jQuery.sap.Version#getMajor
* @public
* @since 1.15.0
* @function
*/
this.getMajor = function() {
return vMajor;
};
/**
* Returns the minor version part of this version.
*
* @return {int} the minor version part of this version
* @name jQuery.sap.Version#getMinor
* @public
* @since 1.15.0
* @function
*/
this.getMinor = function() {
return iMinor;
};
/**
* Returns the patch (or micro) version part of this version.
*
* @return {int} the patch version part of this version
* @name jQuery.sap.Version#getPatch
* @public
* @since 1.15.0
* @function
*/
this.getPatch = function() {
return iPatch;
};
/**
* Returns the version suffix of this version.
*
* @return {string} the version suffix of this version
* @name jQuery.sap.Version#getSuffix
* @public
* @since 1.15.0
* @function
*/
this.getSuffix = function() {
return sSuffix;
};
/**
* Compares this version with a given one.
*
* The version with which this version should be compared can be given as
* <code>jQuery.sap.Version</code> instance, as a string (e.g. <code>v.compareto("1.4.5")</code>)
* or major, minor, patch and suffix cab be given as separate parameters (e.g. <code>v.compareTo(1, 4, 5)</code>)
* or in an array (e.g. <code>v.compareTo([1, 4, 5])</code>).
*
* @return {int} 0, if the given version is equal to this version, a negative value if the given version is greater and a positive value otherwise
* @name jQuery.sap.Version#compareTo
* @public
* @since 1.15.0
* @function
*/
this.compareTo = function() {
var vOther = Version.apply(window, arguments);
/*eslint-disable no-nested-ternary */
return vMajor - vOther.getMajor() ||
iMinor - vOther.getMinor() ||
iPatch - vOther.getPatch() ||
((sSuffix < vOther.getSuffix()) ? -1 : (sSuffix === vOther.getSuffix()) ? 0 : 1);
/*eslint-enable no-nested-ternary */
};
}
/**
* Checks whether this version is in the range of the given versions (start included, end excluded).
*
* The boundaries against which this version should be checked can be given as
* <code>jQuery.sap.Version</code> instances (e.g. <code>v.inRange(v1, v2)</code>), as strings (e.g. <code>v.inRange("1.4", "2.7")</code>)
* or as arrays (e.g. <code>v.inRange([1,4], [2,7])</code>).
*
* @param {string|any[]|jQuery.sap.Version} vMin the start of the range (inclusive)
* @param {string|any[]|jQuery.sap.Version} vMax the end of the range (exclusive)
* @return {boolean} <code>true</code> if this version is greater or equal to <code>vMin</code> and smaller than <code>vMax</code>, <code>false</code> otherwise.
* @name jQuery.sap.Version#inRange
* @public
* @since 1.15.0
* @function
*/
Version.prototype.inRange = function(vMin, vMax) {
return this.compareTo(vMin) >= 0 && this.compareTo(vMax) < 0;
};
// -----------------------------------------------------------------------
var oJQVersion = Version(jQuery.fn.jquery);
if ( !oJQVersion.inRange("1.7.0", "2.0.0") ) {
_earlyLog("error", "SAPUI5 requires a jQuery version of 1.7 or higher, but lower than 2.0; current version is " + jQuery.fn.jquery);
}
// TODO move to a separate module? Only adds 385 bytes (compressed), but...
if ( !jQuery.browser ) {
// re-introduce the jQuery.browser support if missing (jQuery-1.9ff)
jQuery.browser = (function( ua ) {
var rwebkit = /(webkit)[ \/]([\w.]+)/,
ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
rmsie = /(msie) ([\w.]+)/,
rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
ua = ua.toLowerCase(),
match = rwebkit.exec( ua ) ||
ropera.exec( ua ) ||
rmsie.exec( ua ) ||
ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
[],
browser = {};
if ( match[1] ) {
browser[ match[1] ] = true;
browser.version = match[2] || "0";
if ( browser.webkit ) {
browser.safari = true;
}
}
return browser;
}(window.navigator.userAgent));
}
// Fixes the CORS issue (introduced by jQuery 1.7) when loading resources
// (e.g. SAPUI5 script) from other domains for IE browsers.
// The CORS check in jQuery filters out such browsers who do not have the
// property "withCredentials" which is the IE and Opera and prevents those
// browsers to request data from other domains with jQuery.ajax. The CORS
// requests are simply forbidden nevertheless if it works. In our case we
// simply load our script resources from another domain when using the CDN
// variant of SAPUI5. The following fix is also recommended by jQuery:
if (!!sap.ui.Device.browser.internet_explorer) {
jQuery.support = jQuery.support || {};
jQuery.support.cors = true;
}
/**
* Find the script URL where the SAPUI5 is loaded from and return an object which
* contains the identified script-tag and resource root
*/
var _oBootstrap = (function() {
var oTag, sUrl, sResourceRoot,
reConfigurator = /^(.*\/)?download\/configurator[\/\?]/,
reBootScripts = /^(.*\/)?(sap-ui-(core|custom|boot|merged)(-.*)?)\.js([?#]|$)/,
reResources = /^(.*\/)?resources\//;
// check all script tags that have a src attribute
jQuery("script[src]").each(function() {
var src = this.getAttribute("src"),
m;
if ( (m = src.match(reConfigurator)) !== null ) {
// guess 1: script tag src contains "/download/configurator[/?]" (for dynamically created bootstrap files)
oTag = this;
sUrl = src;
sResourceRoot = (m[1] || "") + "resources/";
return false;
} else if ( (m = src.match(reBootScripts)) !== null ) {
// guess 2: src contains one of the well known boot script names
oTag = this;
sUrl = src;
sResourceRoot = m[1] || "";
return false;
} else if ( this.id == 'sap-ui-bootstrap' && (m = src.match(reResources)) ) {
// guess 2: script tag has well known id and src contains "resources/"
oTag = this;
sUrl = src;
sResourceRoot = m[0];
return false;
}
});
return {
tag: oTag,
url: sUrl,
resourceRoot: sResourceRoot
};
})();
/**
* Determine whether sap-bootstrap-debug is set, run debugger statement and allow
* to restart the core from a new URL
*/
(function() {
if (/sap-bootstrap-debug=(true|x|X)/.test(location.search)) {
window["sap-ui-bRestart"] = false;
window["sap-ui-sRestartUrl"] = "http://localhost:8080/sapui5/resources/sap-ui-core.js";
// function to replace the bootstrap tag with a newly created script tag to enable
// restarting the core from a different server
var restartCore = function() {
var oScript = _oBootstrap.tag,
sScript = "<script src=\"" + window["sap-ui-sRestartUrl"] + "\"";
jQuery.each(oScript.attributes, function(i, oAttr) {
if (oAttr.nodeName.indexOf("data-sap-ui-") == 0) {
sScript += " " + oAttr.nodeName + "=\"" + oAttr.nodeValue + "\"";
}
});
sScript += "></script>";
oScript.parentNode.removeChild(oScript);
// clean up cachebuster stuff
jQuery("#sap-ui-bootstrap-cachebusted").remove();
window["sap-ui-config"] && window["sap-ui-config"].resourceRoots && (window["sap-ui-config"].resourceRoots[""] = undefined);
document.write(sScript);
var oRestart = new Error("Aborting UI5 bootstrap and restarting from: " + window["sap-ui-sRestartUrl"]);
oRestart.name = "Restart";
// clean up
delete window["sap-ui-bRestart"];
delete window["sap-ui-sRestartUrl"];
throw oRestart;
};
// debugger stops here. To restart UI5 from somewhere else (default: localhost), set:
// window["sap-ui-bRestart"] = true
// If you want to restart from a different server than localhost, you can adapt the URL, e.g.:
// window["sap-ui-sRestartUrl"] = "http://someserver:8080/sapui5/resources/sap-ui-core.js"
/*eslint-disable no-debugger */
debugger;
/*eslint-enable no-debugger */
if (window["sap-ui-bRestart"]) {
restartCore();
}
}
})();
/**
* Determine whether to use debug sources depending on URL parameter and local storage
* and load debug library if necessary
*/
(function() {
//Check URI param
var bDebugSources = /sap-ui-debug=(true|x|X)/.test(location.search),
bIsOptimized = window["sap-ui-optimized"];
//Check local storage
try { //Necessary for FF when Cookies are deactivated
bDebugSources = bDebugSources || (window.localStorage.getItem("sap-ui-debug") == "X");
} catch (e) {}
window["sap-ui-debug"] = bDebugSources;
// if bootstap URL already contains -dbg URL, just set sap-ui-loaddbg
if (/-dbg\.js([?#]|$)/.test(_oBootstrap.url)) {
window["sap-ui-loaddbg"] = true;
window["sap-ui-debug"] = true;
}
// if current sources are optimized and debug sources are wanted, restart with debug URL
if (bIsOptimized && bDebugSources) {
var sDebugUrl = _oBootstrap.url.replace(/\/(?:sap-ui-cachebuster\/)?([^\/]+)\.js/, "/$1-dbg.js");
window["sap-ui-optimized"] = false;
window["sap-ui-loaddbg"] = true;
document.write("<script type=\"text/javascript\" src=\"" + sDebugUrl + "\"></script>");
var oRestart = new Error("Aborting UI5 bootstrap and restarting from: " + sDebugUrl);
oRestart.name = "Restart";
throw oRestart;
}
})();
/*
* Merged, raw (un-interpreted) configuration data from the following sources
* (last one wins)
* <ol>
* <li>global configuration object <code>window["sap-ui-config"]</code> (could be either a string/url or a configuration object)</li>
* <li><code>data-sap-ui-config</code> attribute of the bootstrap script tag</li>
* <li>other <code>data-sap-ui-<i>xyz</i></code> attributes of the bootstrap tag</li>
* </ol>
*/
var oCfgData = _window["sap-ui-config"] = (function() {
function normalize(o) {
jQuery.each(o, function(i, v) {
var il = i.toLowerCase();
if ( !o.hasOwnProperty(il) ) {
o[il] = v;
delete o[i];
}
});
return o;
}
var oScriptTag = _oBootstrap.tag,
oCfg = _window["sap-ui-config"],
sCfgFile = "sap-ui-config.json";
// load the configuration from an external JSON file
if (typeof oCfg === "string") {
_earlyLog("warning", "Loading external bootstrap configuration from \"" + oCfg + "\". This is a design time feature and not for productive usage!");
if (oCfg !== sCfgFile) {
_earlyLog("warning", "The external bootstrap configuration file should be named \"" + sCfgFile + "\"!");
}
jQuery.ajax({
url : oCfg,
dataType : 'json',
async : false,
success : function(oData, sTextStatus, jqXHR) {
oCfg = oData;
},
error : function(jqXHR, sTextStatus, oError) {
_earlyLog("error", "Loading externalized bootstrap configuration from \"" + oCfg + "\" failed! Reason: " + oError + "!");
oCfg = undefined;
}
});
}
oCfg = normalize(oCfg || {});
oCfg.resourceroots = oCfg.resourceroots || {};
oCfg.themeroots = oCfg.themeroots || {};
oCfg.resourceroots[''] = oCfg.resourceroots[''] || _oBootstrap.resourceRoot;
oCfg['xx-loadallmode'] = /(^|\/)(sap-?ui5|[^\/]+-all).js([?#]|$)/.test(_oBootstrap.url);
// if a script tag has been identified, collect its configuration info
if ( oScriptTag ) {
// evaluate the config attribute first - if present
var sConfig = oScriptTag.getAttribute("data-sap-ui-config");
if ( sConfig ) {
try {
/*eslint-disable no-new-func */
jQuery.extend(oCfg, normalize((new Function("return {" + sConfig + "};"))())); // TODO jQuery.parseJSON would be better but imposes unwanted restrictions on valid syntax
/*eslint-enable no-new-func */
} catch (e) {
// no log yet, how to report this error?
_earlyLog("error", "failed to parse data-sap-ui-config attribute: " + (e.message || e));
}
}
// merge with any existing "data-sap-ui-" attributes
jQuery.each(oScriptTag.attributes, function(i, attr) {
var m = attr.name.match(/^data-sap-ui-(.*)$/);
if ( m ) {
// the following (deactivated) conversion would implement multi-word names like "resource-roots"
m = m[1].toLowerCase(); // .replace(/\-([a-z])/g, function(s,w) { return w.toUpperCase(); })
if ( m === 'resourceroots' ) {
// merge map entries instead of overwriting map
jQuery.extend(oCfg[m], jQuery.parseJSON(attr.value));
} else if ( m === 'theme-roots' ) {
// merge map entries, but rename to camelCase
jQuery.extend(oCfg.themeroots, jQuery.parseJSON(attr.value));
} else if ( m !== 'config' ) {
oCfg[m] = attr.value;
}
}
});
}
return oCfg;
}());
// check whether noConflict must be used...
if ( oCfgData.noconflict === true || oCfgData.noconflict === "true" || oCfgData.noconflict === "x" ) {
jQuery.noConflict();
}
/**
* Root Namespace for the jQuery plug-in provided by SAP SE.
*
* @version 1.28.5
* @namespace
* @public
* @static
*/
jQuery.sap = {};
// -------------------------- VERSION -------------------------------------
jQuery.sap.Version = Version;
// -------------------------- DEBUG LOCAL STORAGE -------------------------------------
jQuery.sap.debug = function(bEnable) {
if (!window.localStorage) {
return null;
}
function reloadHint(bUsesDbgSrc){
/*eslint-disable no-alert */
alert("Usage of debug sources is " + (bUsesDbgSrc ? "on" : "off") + " now.\nFor the change to take effect, you need to reload the page.");
/*eslint-enable no-alert */
}
if (bEnable === true) {
window.localStorage.setItem("sap-ui-debug", "X");
reloadHint(true);
} else if (bEnable === false) {
window.localStorage.removeItem("sap-ui-debug");
reloadHint(false);
}
return window.localStorage.getItem("sap-ui-debug") == "X";
};
// -------------------------- STATISTICS LOCAL STORAGE -------------------------------------
jQuery.sap.statistics = function(bEnable) {
if (!window.localStorage) {
return null;
}
function reloadHint(bUsesDbgSrc){
/*eslint-disable no-alert */
alert("Usage of Gateway statistics " + (bUsesDbgSrc ? "on" : "off") + " now.\nFor the change to take effect, you need to reload the page.");
/*eslint-enable no-alert */
}
if (bEnable === true) {
window.localStorage.setItem("sap-ui-statistics", "X");
reloadHint(true);
} else if (bEnable === false) {
window.localStorage.removeItem("sap-ui-statistics");
reloadHint(false);
}
return window.localStorage.getItem("sap-ui-statistics") == "X";
};
// -------------------------- Logging -------------------------------------
(function() {
var FATAL = 0, ERROR = 1, WARNING = 2, INFO = 3, DEBUG = 4, TRACE = 5,
/**
* Unique prefix for this instance of the core in a multi-frame environment.
*/
sWindowName = (window.top == window) ? "" : "[" + window.location.pathname.split('/').slice(-1)[0] + "] ",
// Note: comparison must use type coercion (==, not ===), otherwise test fails in IE
/**
* The array that holds the log entries that have been recorded so far
*/
aLog = [],
/**
* Maximum log level to be recorded (per component).
*/
mMaxLevel = { '' : ERROR },
/**
* Registered listener to be informed about new log entries.
*/
oListener = null;
function pad0(i,w) {
return ("000" + String(i)).slice(-w);
}
function level(sComponent) {
return (!sComponent || isNaN(mMaxLevel[sComponent])) ? mMaxLevel[''] : mMaxLevel[sComponent];
}
function listener(){
if (!oListener) {
oListener = {
listeners: [],
onLogEntry: function(oLogEntry){
for (var i = 0; i < oListener.listeners.length; i++) {
if (oListener.listeners[i].onLogEntry) {
oListener.listeners[i].onLogEntry(oLogEntry);
}
}
},
attach: function(oLogger, oLstnr){
if (oLstnr) {
oListener.listeners.push(oLstnr);
if (oLstnr.onAttachToLog) {
oLstnr.onAttachToLog(oLogger);
}
}
},
detach: function(oLogger, oLstnr){
for (var i = 0; i < oListener.listeners.length; i++) {
if (oListener.listeners[i] === oLstnr) {
if (oLstnr.onDetachFromLog) {
oLstnr.onDetachFromLog(oLogger);
}
oListener.listeners.splice(i,1);
return;
}
}
}
};
}
return oListener;
}
/**
* Creates a new log entry depending on its level and component.
*
* If the given level is higher than the max level for the given component
* (or higher than the global level, if no component is given),
* then no entry is created.
*/
function log(iLevel, sMessage, sDetails, sComponent) {
if (iLevel <= level(sComponent) ) {
var oNow = new Date(),
oLogEntry = {
time : pad0(oNow.getHours(),2) + ":" + pad0(oNow.getMinutes(),2) + ":" + pad0(oNow.getSeconds(),2),
date : pad0(oNow.getFullYear(),4) + "-" + pad0(oNow.getMonth() + 1,2) + "-" + pad0(oNow.getDate(),2),
timestamp: oNow.getTime(),
level : iLevel,
message : String(sMessage || ""),
details : String(sDetails || ""),
component: String(sComponent || "")
};
aLog.push( oLogEntry );
if (oListener) {
oListener.onLogEntry(oLogEntry);
}
/*
* Console Log, also tries to log to the window.console, if available.
*
* Unfortunately, the support for window.console is quite different between the UI5 browsers. The most important differences are:
* - in IE (checked until IE9), the console object does not exist in a window, until the developer tools are opened for that window.
* After opening the dev tools, the console remains available even when the tools are closed again. Only using a new window (or tab)
* restores the old state without console.
* When the console is available, it provides most standard methods, but not debug and trace
* - in FF3.6 the console is not available, until FireBug is opened. It disappears again, when fire bug is closed.
* But when the settings for a web site are stored (convenience), the console remains open
* When the console is available, it supports all relevant methods
* - in FF9.0, the console is always available, but method assert is only available when firebug is open
* - in Webkit browsers, the console object is always available and has all required methods
* - Exception: in the iOS Simulator, console.info() does not exist
*/
/*eslint-disable no-console */
if (window.console) { // in IE and FF, console might not exist; in FF it might even disappear
var logText = oLogEntry.date + " " + oLogEntry.time + " " + sWindowName + oLogEntry.message + " - " + oLogEntry.details + " " + oLogEntry.component;
switch (iLevel) {
case FATAL:
case ERROR: console.error(logText); break;
case WARNING: console.warn(logText); break;
case INFO: console.info ? console.info(logText) : console.log(logText); break; // info not available in iOS simulator
case DEBUG: console.debug ? console.debug(logText) : console.log(logText); break; // debug not available in IE, fallback to log
case TRACE: console.trace ? console.trace(logText) : console.log(logText); break; // trace not available in IE, fallback to log (no trace)
}
}
/*eslint-enable no-console */
return oLogEntry;
}
}
/**
* Creates a new Logger instance which will use the given component string
* for all logged messages without a specific component.
*
* @param {string} sDefaultComponent
*
* @class A Logger class
* @name jQuery.sap.log.Logger
* @since 1.1.2
* @public
*/
function Logger(sDefaultComponent) {
/**
* Creates a new fatal-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log instance for method chaining
* @name jQuery.sap.log.Logger#fatal
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.fatal = function (sMessage, sDetails, sComponent) {
log(FATAL, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Creates a new error-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log instance
* @name jQuery.sap.log.Logger#error
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.error = function error(sMessage, sDetails, sComponent) {
log(ERROR, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Creates a new warning-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log instance
* @name jQuery.sap.log.Logger#warning
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.warning = function warning(sMessage, sDetails, sComponent) {
log(WARNING, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Creates a new info-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log instance
* @name jQuery.sap.log.Logger#info
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.info = function info(sMessage, sDetails, sComponent) {
log(INFO, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Creates a new debug-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log instance
* @name jQuery.sap.log.Logger#debug
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.debug = function debug(sMessage, sDetails, sComponent) {
log(DEBUG, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Creates a new trace-level entry in the log with the given message, details and calling component.
*
* @param {string} sMessage Message text to display
* @param {string} [sDetails=''] Details about the message, might be omitted
* @param {string} [sComponent=''] Name of the component that produced the log entry
* @return {jQuery.sap.log.Logger} The log-instance
* @name jQuery.sap.log.Logger#trace
* @function
* @public
* @SecSink {0 1 2|SECRET} Could expose secret data in logs
*/
this.trace = function trace(sMessage, sDetails, sComponent) {
log(TRACE, sMessage, sDetails, sComponent || sDefaultComponent);
return this;
};
/**
* Defines the maximum jQuery.sap.log.Level of log entries that will be recorded.
* Log entries with a higher (less important) log level will be omitted from the log.
* When a component name is given, the log level will be configured for that component
* only, otherwise the log level for the default component of this logger is set.
* For the global logger, the global default level is set.
*
* <b>Note</b>: Setting a global default log level has no impact on already defined
* component log levels. They always override the global default log level.
*
* @param {jQuery.sap.log.Level} iLogLevel
* @param {string} [sComponent] The log component to set the log level for.
* @return {jQuery.sap.log} The global logger to allow method chaining
* @name jQuery.sap.log.Logger#setLevel
* @function
* @public
*/
this.setLevel = function setLevel(iLogLevel, sComponent) {
sComponent = sComponent || sDefaultComponent || '';
mMaxLevel[sComponent] = iLogLevel;
var mBackMapping = [];
jQuery.each(jQuery.sap.log.LogLevel, function(idx, v){
mBackMapping[v] = idx;
});
log(INFO, "Changing log level " + (sComponent ? "for '" + sComponent + "' " : "") + "to " + mBackMapping[iLogLevel], "", "jQuery.sap.log");
return this;
};
/**
* Returns the log level currently effective for the given component.
* If no component is given or when no level has been configured for a
* given component, the log level for the default component of this logger is returned.
*
* @param {string} [sComponent] Name of the component to retrieve the log level for
* @return {int} The log level for the given component or the default log level
* @name jQuery.sap.log.Logger#getLevel
* @function
* @public
* @since 1.1.2
*/
this.getLevel = function getLevel(sComponent) {
return level(sComponent || sDefaultComponent);
};
/**
* Checks whether logging is enabled for the given log level,
* depending on the currently effective log level for the given component.
*
* If no component is given, the default component of this logger will be taken into account.
*
* @param {int} [iLevel=Level.DEBUG] the log level in question
* @param {string} [sComponent] Name of the component to check the log level for
* @return {boolean} Whether logging is enabled or not
* @name jQuery.sap.log.Logger#isLoggable
* @function
* @public
* @since 1.13.2
*/
this.isLoggable = function (iLevel, sComponent) {
return (iLevel == null ? DEBUG : iLevel) <= level(sComponent || sDefaultComponent);
};
}
/**
* A Logging API for JavaScript.
*
* Provides methods to manage a client-side log and to create entries in it. Each of the logging methods
* {@link jQuery.sap.log.#debug}, {@link jQuery.sap.log.#info}, {@link jQuery.sap.log.#warning},
* {@link jQuery.sap.log.#error} and {@link jQuery.sap.log.#fatal} creates and records a log entry,
* containing a timestamp, a log level, a message with details and a component info.
* The log level will be one of {@link jQuery.sap.log.Level} and equals the name of the concrete logging method.
*
* By using the {@link jQuery.sap.log#setLevel} method, consumers can determine the least important
* log level which should be recorded. Less important entries will be filtered out. (Note that higher numeric
* values represent less important levels). The initially set level depends on the mode that UI5 is running in.
* When the optimized sources are executed, the default level will be {@link jQuery.sap.log.Level.ERROR}.
* For normal (debug sources), the default level is {@link jQuery.sap.log.Level.DEBUG}.
*
* All logging methods allow to specify a <b>component</b>. These components are simple strings and
* don't have a special meaning to the UI5 framework. However they can be used to semantically group
* log entries that belong to the same software component (or feature). There are two APIs that help
* to manage logging for such a component. With <code>{@link jQuery.sap.log.getLogger}(sComponent)</code>,
* one can retrieve a logger that automatically adds the given <code>sComponent</code> as component
* parameter to each log entry, if no other component is specified. Typically, JavaScript code will
* retrieve such a logger once during startup and reuse it for the rest of its lifecycle.
* Second, the {@link jQuery.sap.log.Logger#setLevel}(iLevel, sComponent) method allows to set the log level
* for a specific component only. This allows a more fine granular control about the created logging entries.
* {@link jQuery.sap.log.Logger.getLevel} allows to retrieve the currently effective log level for a given
* component.
*
* {@link jQuery.sap.log#getLog} returns an array of the currently collected log entries.
*
* Furthermore, a listener can be registered to the log. It will be notified whenever a new entry
* is added to the log. The listener can be used for displaying log entries in a separate page area,
* or for sending it to some external target (server).
*
* @author SAP SE
* @since 0.9.0
* @namespace
* @public
* @borrows jQuery.sap.log.Logger#fatal as fatal
* @borrows jQuery.sap.log.Logger#error as error
* @borrows jQuery.sap.log.Logger#warning as warning
* @borrows jQuery.sap.log.Logger#info as info
* @borrows jQuery.sap.log.Logger#debug as debug
* @borrows jQuery.sap.log.Logger#trace as trace
* @borrows jQuery.sap.log.Logger#getLevel as getLevel
* @borrows jQuery.sap.log.Logger#setLevel as setLevel
* @borrows jQuery.sap.log.Logger#isLoggable as isLoggable
*/
jQuery.sap.log = jQuery.extend(new Logger(), /** @lends jQuery.sap.log */ {
/**
* Enumeration of the configurable log levels that a Logger should persist to the log.
*
* Only if the current LogLevel is higher than the level {@link jQuery.sap.log.Level} of the currently added log entry,
* then this very entry is permanently added to the log. Otherwise it is ignored.
* @see jQuery.sap.log.Logger#setLevel
* @namespace
* @public
*/
Level : {
/**
* Do not log anything
* @public
*/
NONE : FATAL - 1,
/**
* Fatal level. Use this for logging unrecoverable situations
* @public
*/
FATAL : FATAL,
/**
* Error level. Use this for logging of erroneous but still recoverable situations
* @public
*/
ERROR : ERROR,
/**
* Warning level. Use this for logging unwanted but foreseen situations
* @public
*/
WARNING : WARNING,
/**
* Info level. Use this for logging information of purely informative nature
* @public
*/
INFO : INFO,
/**
* Debug level. Use this for logging information necessary for debugging
* @public
*/
DEBUG : DEBUG,
/**
* Trace level. Use this for tracing the program flow.
* @public
*/
TRACE : TRACE, /* TODO Think about changing to 10 and thus to pull out of logging... -> Make tracing explicit */
/**
* Trace level to log everything.
*/
ALL : (TRACE + 1) /* TODO if TRACE is changed to make sure this is 6 again. There would then be some special TRACE handling. */
},
/**
* Returns a {@link jQuery.sap.log.Logger} for the given component.
*
* The method might or might not return the same logger object across multiple calls.
* While loggers are assumed to be light weight objects, consumers should try to
* avoid redundant calls and instead keep references to already retrieved loggers.
*
* The optional second parameter <code>iDefaultLogLevel</code> allows to specify
* a default log level for the component. It is only applied when no log level has been
* defined so far for that component (ignoring inherited log levels). If this method is
* called multiple times for the same component but with different log levels,
* only the first call one might be taken into account.
*
* @param {string} sComponent Component to create the logger for
* @param {int} [iDefaultLogLevel] a default log level to be used for the component,
* if no log level has been defined for it so far.
* @return {jQuery.sap.log.Logger} A logger for the component.
* @public
* @static
* @since 1.1.2
*/
getLogger : function(sComponent, iDefaultLogLevel) {
if ( !isNaN(iDefaultLogLevel) && mMaxLevel[sComponent] == null ) {
mMaxLevel[sComponent] = iDefaultLogLevel;
}
return new Logger(sComponent);
},
/**
* Returns the logged entries recorded so far as an array.
*
* Log entries are plain JavaScript objects with the following properties
* <ul>
* <li>timestamp {number} point in time when the entry was created
* <li>level {int} LogLevel level of the entry
* <li>message {string} message text of the entry
* </ul>
*
* @return {object[]} an array containing the recorded log entries
* @public
* @static
* @since 1.1.2
*/
getLogEntries : function () {
return aLog.slice();
},
/**
* Allows to add a new LogListener that will be notified for new log entries.
* The given object must provide method <code>onLogEntry</code> and can also be informed
* about <code>onDetachFromLog</code> and <code>onAttachToLog</code>
* @param {object} oListener The new listener object that should be informed
* @return {jQuery.sap.log} The global logger
* @public
* @static
*/
addLogListener : function(oListener) {
listener().attach(this, oListener);
return this;
},
/**
* Allows to remove a registered LogListener.
* @param {object} oListener The new listener object that should be removed
* @return {jQuery.sap.log} The global logger
* @public
* @static
*/
removeLogListener : function(oListener) {
listener().detach(this, oListener);
return this;
}
});
/**
* Enumeration of levels that can be used in a call to {@link jQuery.sap.log.Logger#setLevel}(iLevel, sComponent).
*
* @deprecated Since 1.1.2. To streamline the Logging API a bit, the separation between Level and LogLevel has been given up.
* Use the (enriched) enumeration {@link jQuery.sap.log.Level} instead.
* @namespace
* @public
*/
jQuery.sap.log.LogLevel = jQuery.sap.log.Level;
/**
* Retrieves the currently recorded log entries.
* @deprecated Since 1.1.2. To avoid confusion with getLogger, this method has been renamed to {@link jQuery.sap.log.getLogEntries}.
* @function
* @public
* @static
*/
jQuery.sap.log.getLog = jQuery.sap.log.getLogEntries;
/**
* A simple assertion mechanism that logs a message when a given condition is not met.
*
* <b>Note:</b> Calls to this method might be removed when the JavaScript code
* is optimized during build. Therefore, callers should not rely on any side effects
* of this method.
*
* @param {boolean} bResult result of the checked assertion
* @param {string} sMessage message that will be raised when the result is <code>false</code>
*
* @public
* @static
* @SecSink {1|SECRET} Could expose secret data in logs
*/
jQuery.sap.assert = function(bResult, sMessage) {
if ( !bResult ) {
/*eslint-disable no-console */
if ( window.console && console.assert ) {
console.assert(bResult, sWindowName + sMessage);
} else {
// console is not always available (IE, FF) and IE doesn't support console.assert
jQuery.sap.log.debug("[Assertions] " + sMessage);
}
/*eslint-enable no-console */
}
};
// against all our rules: use side effect of assert to differentiate between optimized and productive code
jQuery.sap.assert( !!(mMaxLevel[''] = DEBUG), "will be removed in optimized version");
// evaluate configuration
oCfgData.loglevel = (function() {
var m = /(?:\?|&)sap-ui-log(?:L|-l)evel=([^&]*)/.exec(window.location.search);
return m && m[1];
}()) || oCfgData.loglevel;
if ( oCfgData.loglevel ) {
jQuery.sap.log.setLevel(jQuery.sap.log.Level[oCfgData.loglevel.toUpperCase()] || parseInt(oCfgData.loglevel,10));
}
jQuery.sap.log.info("SAP Logger started.");
// log early logs
jQuery.each(_earlyLogs, function(i,e) {
jQuery.sap.log[e.level](e.message);
});
_earlyLogs = null;
}());
// ---------------------------------------------------------------------------------------------------
/**
* Returns a new constructor function that creates objects with
* the given prototype.
*
* @param {object} oPrototype
* @return {function} the newly created constructor function
* @public
* @static
*/
jQuery.sap.factory = function factory(oPrototype) {
function Factory() {}
Factory.prototype = oPrototype;
return Factory;
};
/**
* Returns a new object which has the given oPrototype as its prototype.
*
* If several objects with the same prototype are to be created,
* {@link jQuery.sap.factory} should be used instead.
*
* @param {object} oPrototype
* @return {object} new object
* @public
* @static
*/
jQuery.sap.newObject = function newObject(oPrototype) {
return new (jQuery.sap.factory(oPrototype))();
};
/**
* Returns a new function that returns the given <code>oValue</code> (using its closure).
*
* Avoids the need for a dedicated member for the value.
*
* As closures don't come for free, this function should only be used when polluting
* the enclosing object is an absolute "must-not" (as it is the case in public base classes).
*
* @param {object} oValue
*
* @public
* @static
*/
jQuery.sap.getter = function getter(oValue) {
return function() {
return oValue;
};
};
/**
* Returns a JavaScript object which is identified by a sequence of names.
*
* A call to <code>getObject("a.b.C")</code> has essentially the same effect
* as accessing <code>window.a.b.C</code> but with the difference that missing
* intermediate objects (a or b in the example above) don't lead to an exception.
*
* When the addressed object exists, it is simply returned. If it doesn't exists,
* the behavior depends on the value of the second, optional parameter
* <code>iNoCreates</code> (assuming 'n' to be the number of names in the name sequence):
* <ul>
* <li>NaN: if iNoCreates is not a number and the addressed object doesn't exist,
* then <code>getObject()</code> returns <code>undefined</code>.
* <li>0 &lt; iNoCreates &lt; n: any non-existing intermediate object is created, except
* the <i>last</i> <code>iNoCreates</code> ones.
* </ul>
*
* Example:
* <pre>
* getObject() -- returns the context object (either param or window)
* getObject("a.b.C") -- will only try to get a.b.C and return undefined if not found.
* getObject("a.b.C", 0) -- will create a, b, and C in that order if they don't exists
* getObject("a.b.c", 1) -- will create a and b, but not C.
* </pre>
*
* When a <code>oContext</code> is given, the search starts in that object.
* Otherwise it starts in the <code>window</code> object that this plugin
* has been created in.
*
* Note: Although this method internally uses <code>object["key"]</code> to address object
* properties, it does not support all possible characters in a name.
* Especially the dot ('.') is not supported in the individual name segments,
* as it is always interpreted as a name separator.
*
* @param {string} sName a dot separated sequence of names that identify the required object
* @param {int} [iNoCreates=NaN] number of objects (from the right) that should not be created
* @param {object} [oContext=window] the context to execute the search in
*
* @public
* @static
*/
jQuery.sap.getObject = function getObject(sName, iNoCreates, oContext) {
var oObject = oContext || _window,
aNames = (sName || "").split("."),
l = aNames.length,
iEndCreate = isNaN(iNoCreates) ? 0 : l - iNoCreates,
i;
for (i = 0; oObject && i < l; i++) {
if (!oObject[aNames[i]] && i < iEndCreate ) {
oObject[aNames[i]] = {};
}
oObject = oObject[aNames[i]];
}
return oObject;
};
/**
* Sets an object property to a given value, where the property is
* identified by a sequence of names (path).
*
* When a <code>oContext</code> is given, the path starts in that object.
* Otherwise it starts in the <code>window</code> object that this plugin
* has been created for.
*
* Note: Although this method internally uses <code>object["key"]</code> to address object
* properties, it does not support all possible characters in a name.
* Especially the dot ('.') is not supported in the individual name segments,
* as it is always interpreted as a name separator.
*
* @param {string} sName a dot separated sequence of names that identify the property
* @param {any} vValue value to be set, can have any type
* @param {object} [oContext=window] the context to execute the search in
* @public
* @static
*/
jQuery.sap.setObject = function (sName, vValue, oContext) {
var oObject = oContext || _window,
aNames = (sName || "").split("."),
l = aNames.length, i;
if ( l > 0 ) {
for (i = 0; oObject && i < l - 1; i++) {
if (!oObject[aNames[i]] ) {
oObject[aNames[i]] = {};
}
oObject = oObject[aNames[i]];
}
oObject[aNames[l - 1]] = vValue;
}
};
// ---------------------- sync point -------------------------------------------------------------
/*
* Internal class that can help to synchronize a set of asynchronous tasks.
* Each task must be registered in the sync point by calling startTask with
* an (purely informative) title. The returned value must be used in a later
* call to finishTask.
* When finishTask has been called for all tasks that have been started,
* the fnCallback will be fired.
* When a timeout is given and reached, the callback is called at that
* time, no matter whether all tasks have been finished or not.
*/
function SyncPoint(sName, fnCallback, iTimeout) {
var aTasks = [],
iOpenTasks = 0,
iFailures = 0,
sTimer;
this.startTask = function(sTitle) {
var iId = aTasks.length;
aTasks[iId] = { name : sTitle, finished : false };
iOpenTasks++;
return iId;
};
this.finishTask = function(iId, bSuccess) {
if ( !aTasks[iId] || aTasks[iId].finished ) {
throw new Error("trying to finish non existing or already finished task");
}
aTasks[iId].finished = true;
iOpenTasks--;
if ( bSuccess === false ) {
iFailures++;
}
if ( iOpenTasks === 0 ) {
jQuery.sap.log.info("Sync point '" + sName + "' finished (tasks:" + aTasks.length + ", open:" + iOpenTasks + ", failures:" + iFailures + ")");
if ( sTimer ) {
clearTimeout(sTimer);
sTimer = null;
}
finish();
}
};
function finish() {
fnCallback && fnCallback(iOpenTasks, iFailures);
fnCallback = null;
}
if ( !isNaN(iTimeout) ) {
sTimer = setTimeout(function() {
jQuery.sap.log.info("Sync point '" + sName + "' timed out (tasks:" + aTasks.length + ", open:" + iOpenTasks + ", failures:" + iFailures + ")");
finish();
}, iTimeout);
}
jQuery.sap.log.info("Sync point '" + sName + "' created" + (iTimeout ? "(timeout after " + iTimeout + " ms)" : ""));
}
/**
* Internal function to create a sync point.
* @private
*/
jQuery.sap.syncPoint = function(sName, fnCallback, iTimeout) {
return new SyncPoint(sName, fnCallback, iTimeout);
};
// ---------------------- require/declare --------------------------------------------------------
var getModuleSystemInfo = (function() {
/**
* Local logger, by default only logging errors. Can be configured to DEBUG via config parameter.
* @private
*/
var log = jQuery.sap.log.getLogger("sap.ui.ModuleSystem",
(/sap-ui-xx-debug(M|-m)odule(L|-l)oading=(true|x|X)/.test(location.search) || oCfgData["xx-debugModuleLoading"]) ? jQuery.sap.log.Level.DEBUG : jQuery.sap.log.Level.INFO
),
/**
* A map of URL prefixes keyed by the corresponding module name prefix.
* URL prefix can either be given as string or as object with properties url and final.
* When final is set to true, module name prefix cannot be overwritten.
* @see jQuery.sap.registerModulePath
*
* Note that the empty prefix ('') will always match and thus serves as a fallback.
* @private
*/
mUrlPrefixes = { '' : { 'url' : 'resources/' } },
/**
* Module neither has been required nor preloaded not declared, but someone asked for it.
*/
INITIAL = 0,
/**
* Module has been preloaded, but not required or declared
*/
PRELOADED = -1,
/**
* Module has been declared.
*/
LOADING = 1,
/**
* Module has been loaded, but not yet executed.
*/
LOADED = 2,
/**
* Module is currently being executed
*/
EXECUTING = 3,
/**
* Module has been loaded and executed without errors.
*/
READY = 4,
/**
* Module either could not be loaded or execution threw an error
*/
FAILED = 5,
/**
* Set of modules that have been loaded (required) so far.
*
* Each module is an object that can have the following members
* <ul>
* <li>{int} state one of the module states defined in this function
* <li>{string} url URL where the module has been loaded from
* <li>{any} data temp. raw content of the module (between loaded and ready)
* <li>{string} error an error description for state <code>FAILED</code>
* <li>{any} content the content of the module as exported via define()
* </ul>
* @private
*/
mModules = {
// predefine already loaded modules to avoid redundant loading
// "sap/ui/thirdparty/jquery/jquery-1.7.1.js" : { state : READY, url : _sBootstrapUrl, content : jQuery },
"sap/ui/thirdparty/URI.js" : { state : READY, url : _sBootstrapUrl, content : URI },
"sap/ui/Device.js" : { state : READY, url : _sBootstrapUrl, content : sap.ui.Device },
"jquery.sap.global.js" : { state : READY, url : _sBootstrapUrl, content : jQuery }
},
mPreloadModules = {},
/**
* Information about third party modules that react on AMD loaders and need a workaround
* to be able to work with jQuery.sap.require no matter whether an AMD loader is present or not.
*
* Note: this is a map for future extension
* Note: should be maintained together with raw-module info in .library files
* @private
*/
mAMDShim = {
'sap/ui/thirdparty/blanket.js': true,
'sap/ui/thirdparty/crossroads.js': true,
'sap/ui/thirdparty/d3.js': true,
'sap/ui/thirdparty/datajs.js': true,
'sap/ui/thirdparty/handlebars.js': true,
'sap/ui/thirdparty/hasher.js': true,
'sap/ui/thirdparty/IPv6.js': true,
'sap/ui/thirdparty/jquery/jquery-1.11.1.js': true,
'sap/ui/thirdparty/jquery/jquery-1.10.2.js': true,
'sap/ui/thirdparty/jquery/jquery-1.10.1.js': true,
'sap/ui/thirdparty/jquery/jquery.1.7.1.js': true,
'sap/ui/thirdparty/jquery/jquery.1.8.1.js': true,
'sap/ui/thirdparty/jquery-mobile-custom.js': true,
'sap/ui/thirdparty/jszip.js': true,
'sap/ui/thirdparty/less.js': true,
'sap/ui/thirdparty/punycode.js': true,
'sap/ui/thirdparty/require.js': true,
'sap/ui/thirdparty/SecondLevelDomains.js': true,
'sap/ui/thirdparty/signals.js': true,
'sap/ui/thirdparty/URI.js' : true,
'sap/ui/thirdparty/URITemplate.js' : true,
'sap/ui/demokit/js/esprima.js' : true
},
/**
* Stack of modules that are currently executed.
*
* Allows to identify the containing module in case of multi module files (e.g. sap-ui-core)
* @private
*/
_execStack = [ ],
/**
* A prefix that will be added to module loading log statements and which reflects the nesting of module executions.
* @private
*/
sLogPrefix = "",
// max size a script should have when executing it with execScript (IE). Otherwise fallback to eval
MAX_EXEC_SCRIPT_LENGTH = 512 * 1024,
sDocumentLocation = document.location.href.replace(/\?.*|#.*/g, ""),
FRAGMENT = "fragment",
VIEW = "view",
mKnownSubtypes = {
js : [VIEW, FRAGMENT, "controller", "designtime"],
xml: [VIEW, FRAGMENT],
json: [VIEW, FRAGMENT],
html: [VIEW, FRAGMENT]
},
rJSSubtypes = new RegExp("(\\.(?:" + mKnownSubtypes.js.join("|") + "))?\\.js$"),
rTypes,
rSubTypes;
(function() {
var s = "",
sSub = "";
jQuery.each(mKnownSubtypes, function(sType, aSubtypes) {
s = (s ? s + "|" : "") + sType;
sSub = (sSub ? sSub + "|" : "") + "(?:(?:" + aSubtypes.join("\\.|") + "\\.)?" + sType + ")";
});
s = "\\.(" + s + ")$";
sSub = "\\.(?:" + sSub + "|[^./]+)$";
log.debug("constructed regexp for file types :" + s);
log.debug("constructed regexp for file sub-types :" + sSub);
rTypes = new RegExp(s);
rSubTypes = new RegExp(sSub);
}());
/**
* Name conversion function that converts a name in UI5 module name syntax to a name in requireJS module name syntax.
* @private
*/
function ui5ToRJS(sName) {
if ( /^sap\.ui\.thirdparty\.jquery\.jquery-/.test(sName) ) {
return "sap/ui/thirdparty/jquery/jquery-" + sName.slice("sap.ui.thirdparty.jquery.jquery-".length);
} else if ( /^jquery\.sap\./.test(sName) ) {
return sName;
}
return sName.replace(/\./g, "/");
}
/**
* Name conversion function that converts a name in unified resource name syntax to a name in UI5 module name syntax.
* If the name cannot be converted (e.g. doesn't end with '.js'), then <code>undefined</code> is returned.
*
* @private
*/
function urnToUI5(sName) {
// UI5 module name syntax is only defined for JS resources
if ( !/\.js$/.test(sName) ) {
return;
}
sName = sName.slice(0, -3);
if ( /^sap\/ui\/thirdparty\/jquery\/jquery-/.test(sName) ) {
return "sap.ui.thirdparty.jquery.jquery-" + sName.slice("sap/ui/thirdparty/jquery/jquery-".length);
} else if ( /^jquery\.sap\./.test(sName) ) {
return sName; // do nothing
}
return sName.replace(/\//g, ".");
}
// find longest matching prefix for resource name
function getResourcePath(sResourceName, sSuffix) {
// split name into segments
var aSegments = sResourceName.split(/\//),
l, sNamePrefix, sResult, m;
// if no suffix was given and if the name is not empty, try to guess the suffix from the last segment
if ( arguments.length === 1 && aSegments.length > 0 ) {
// only known types (and their known subtypes) are accepted
m = rSubTypes.exec(aSegments[aSegments.length - 1]);
if ( m ) {
sSuffix = m[0];
aSegments[aSegments.length - 1] = aSegments[aSegments.length - 1].slice(0, m.index);
} else {
sSuffix = "";
}
}
// search for a defined name prefix, starting with the full name and successively removing one segment
for (l = aSegments.length; l >= 0; l--) {
sNamePrefix = aSegments.slice(0, l).join('/');
if ( mUrlPrefixes[sNamePrefix] ) {
sResult = mUrlPrefixes[sNamePrefix].url;
if ( l < aSegments.length ) {
sResult += aSegments.slice(l).join('/');
}
if ( sResult.slice(-1) === '/' ) {
sResult = sResult.slice(0, -1);
}
return sResult + (sSuffix || '');
}
}
jQuery.sap.assert(false, "should never happen");
}
function guessResourceName(sURL) {
var sNamePrefix,
sUrlPrefix,
sResourceName;
for (sNamePrefix in mUrlPrefixes) {
if ( mUrlPrefixes.hasOwnProperty(sNamePrefix) ) {
// Note: configured URL prefixes are guaranteed to end with a '/'
// But to support the legacy scenario promoted by the application tools ( "registerModulePath('Application','Application')" )
// the prefix check here has to be done without the slash
sUrlPrefix = mUrlPrefixes[sNamePrefix].url.slice(0, -1);
if ( sURL.indexOf(sUrlPrefix) === 0 ) {
// calc resource name
sResourceName = sNamePrefix + sURL.slice(sUrlPrefix.length);
// remove a leading '/' (occurs if name prefix is empty and if match was a full segment match
if ( sResourceName.charAt(0) === '/' ) {
sResourceName = sResourceName.slice(1);
}
if ( mModules[sResourceName] && mModules[sResourceName].data ) {
return sResourceName;
}
}
}
}
// return undefined;
}
function declareModule(sModuleName) {
var oModule;
// sModuleName must be a unified resource name of type .js
jQuery.sap.assert(/\.js$/.test(sModuleName), "must be a Javascript module");
oModule = mModules[sModuleName] || (mModules[sModuleName] = { state : INITIAL });
if ( oModule.state > INITIAL ) {
return oModule;
}
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "declare module '" + sModuleName + "'");
}
// avoid cycles
oModule.state = READY;
// the first call to declareModule is assumed to identify the bootstrap module
// Note: this is only a guess and fails e.g. when multiple modules are loaded via a script tag
// to make it safe, we could convert 'declare' calls to e.g. 'subdeclare' calls at build time.
if ( _execStack.length === 0 ) {
_execStack.push(sModuleName);
oModule.url = oModule.url || _sBootstrapUrl;
}
return oModule;
}
function requireModule(sModuleName) {
var m = rJSSubtypes.exec(sModuleName),
sBaseName, sType, oModule, aExtensions, i;
// only for robustness, should not be possible by design (all callers append '.js')
if ( !m ) {
log.error("can only require Javascript module, not " + sModuleName);
return;
}
// in case of having a type specified ignore the type for the module path creation and add it as file extension
sBaseName = sModuleName.slice(0, m.index);
sType = m[0]; // must be a normalized resource name of type .js sType can be empty or one of view|controller|fragment
oModule = mModules[sModuleName] || (mModules[sModuleName] = { state : INITIAL });
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "require '" + sModuleName + "' of type '" + sType + "'");
}
// check if module has been loaded already
if ( oModule.state !== INITIAL ) {
if ( oModule.state === PRELOADED ) {
oModule.state = LOADED;
execModule(sModuleName);
}
if ( oModule.state === READY ) {
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "module '" + sModuleName + "' has already been loaded (skipped).");
}
return this;
} else if ( oModule.state === FAILED ) {
throw new Error("found in negative cache: '" + sModuleName + "' from " + oModule.url + ": " + oModule.error);
} else {
// currently loading
return this;
}
}
// set marker for loading modules (to break cycles)
oModule.state = LOADING;
// if debug is enabled, try to load debug module first
aExtensions = window["sap-ui-loaddbg"] ? ["-dbg", ""] : [""];
for (i = 0; i < aExtensions.length && oModule.state !== LOADED; i++) {
// create module URL for the current extension
oModule.url = getResourcePath(sBaseName, aExtensions[i] + sType);
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "loading " + (aExtensions[i] ? aExtensions[i] + " version of " : "") + "'" + sModuleName + "' from '" + oModule.url + "'");
}
/*eslint-disable no-loop-func */
jQuery.ajax({
url : oModule.url,
dataType : 'text',
async : false,
success : function(response, textStatus, xhr) {
oModule.state = LOADED;
oModule.data = response;
},
error : function(xhr, textStatus, error) {
oModule.state = FAILED;
oModule.error = xhr ? xhr.status + " - " + xhr.statusText : textStatus;
}
});
/*eslint-enable no-loop-func */
}
// execute module __after__ loading it, this reduces the required stack space!
if ( oModule.state === LOADED ) {
execModule(sModuleName);
}
if ( oModule.state !== READY ) {
throw new Error("failed to load '" + sModuleName + "' from " + oModule.url + ": " + oModule.error);
}
}
// sModuleName must be a normalized resource name of type .js
function execModule(sModuleName) {
var oModule = mModules[sModuleName],
sOldPrefix, sScript, vAMD;
if ( oModule && oModule.state === LOADED && typeof oModule.data !== "undefined" ) {
// check whether the module is known to use an existing AMD loader, remember the AMD flag
vAMD = mAMDShim[sModuleName] && typeof window.define === "function" && window.define.amd;
try {
if ( vAMD ) {
// temp. remove the AMD Flag from the loader
delete window.define.amd;
}
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "executing '" + sModuleName + "'");
sOldPrefix = sLogPrefix;
sLogPrefix = sLogPrefix + ": ";
}
// execute the script in the window context
oModule.state = EXECUTING;
_execStack.push(sModuleName);
if ( typeof oModule.data === "function" ) {
oModule.data.call(window);
} else {
sScript = oModule.data;
// sourceURL: Firebug, Chrome, Safari and IE11 debugging help, appending the string seems to cost ZERO performance
// Note: IE11 supports sourceURL even when running in IE9 or IE10 mode
// Note: make URL absolute so Chrome displays the file tree correctly
// Note: do not append if there is already a sourceURL / sourceMappingURL
if (sScript && !sScript.match(/\/\/[#@] source(Mapping)?URL=.*$/)) {
sScript += "\n//# sourceURL=" + URI(oModule.url).absoluteTo(sDocumentLocation);
}
// framework internal hook to intercept the loaded script and modify
// it before executing the script - e.g. useful for client side coverage
if (typeof jQuery.sap.require._hook === "function") {
sScript = jQuery.sap.require._hook(sScript, sModuleName);
}
if (_window.execScript && (!oModule.data || oModule.data.length < MAX_EXEC_SCRIPT_LENGTH) ) {
try {
oModule.data && _window.execScript(sScript); // execScript fails if data is empty
} catch (e) {
_execStack.pop();
// eval again with different approach - should fail with a more informative exception
jQuery.sap.globalEval(oModule.data);
throw e; // rethrow err in case globalEval succeeded unexpectedly
}
} else {
_window.eval(sScript);
}
}
_execStack.pop();
oModule.state = READY;
oModule.data = undefined;
// best guess for legacy modules that don't use sap.ui.define
// TODO implement fallback for raw modules
oModule.content = oModule.content || jQuery.sap.getObject(urnToUI5(sModuleName));
if ( log.isLoggable() ) {
sLogPrefix = sOldPrefix;
log.debug(sLogPrefix + "finished executing '" + sModuleName + "'");
}
} catch (err) {
oModule.state = FAILED;
oModule.error = ((err.toString && err.toString()) || err.message) + (err.line ? "(line " + err.line + ")" : "" );
oModule.data = undefined;
if ( window["sap-ui-debug"] && (/sap-ui-xx-show(L|-l)oad(E|-e)rrors=(true|x|X)/.test(location.search) || oCfgData["xx-showloaderrors"]) ) {
log.error("error while evaluating " + sModuleName + ", embedding again via script tag to enforce a stack trace (see below)");
jQuery.sap.includeScript(oModule.url);
return;
}
} finally {
// restore AMD flag
if ( vAMD ) {
window.define.amd = vAMD;
}
}
}
}
function requireAll(aDependencies, fnCallback) {
var aModules = [],
i, sDepModName;
for (i = 0; i < aDependencies.length; i++) {
sDepModName = aDependencies[i];
log.debug(sLogPrefix + "require '" + sDepModName + "'");
requireModule(sDepModName + ".js");
// best guess for legacy modules that don't use sap.ui.define
// TODO implement fallback for raw modules
aModules[i] = mModules[sDepModName + ".js"].content || jQuery.sap.getObject(urnToUI5(sDepModName + ".js"));
log.debug(sLogPrefix + "require '" + sDepModName + "': done.");
}
fnCallback(aModules);
}
/**
* Constructs an URL to load the module with the given name and file type (suffix).
*
* Searches the longest prefix of the given module name for which a registration
* exists (see {@link jQuery.sap.registerModulePath}) and replaces that prefix
* by the registered URL prefix.
*
* The remainder of the module name is appended to the URL, replacing any dot with a slash.
*
* Finally, the given suffix (typically a file name extension) is added (unconverted).
*
* The returned name (without the suffix) doesn't end with a slash.
*
* @param {string} sModuleName module name to detemrine the path for
* @param {string} sSuffix suffix to be added to the resulting path
* @return {string} calculated path (URL) to the given module
*
* @public
* @static
*/
jQuery.sap.getModulePath = function(sModuleName, sSuffix) {
return getResourcePath(ui5ToRJS(sModuleName), sSuffix);
};
/**
* Determines the URL for a resource given its unified resource name.
*
* Searches the longest prefix of the given resource name for which a registration
* exists (see {@link jQuery.sap.registerResourcePath}) and replaces that prefix
* by the registered URL prefix.
*
* The remainder of the resource name is appended to the URL.
*
* <b>Unified Resource Names</b>
* Several UI5 APIs use <i>Unified Resource Names (URNs)</i> as naming scheme for resources that
* they deal with (e.h. Javascript, CSS, JSON, XML, ...). URNs are similar to the path
* component of an URL:
* <ul>
* <li>they consist of a non-empty sequence of name segments</li>
* <li>segments are separated by a forward slash '/'</li>
* <li>name segments consist of URL path segment characters only. It is recommened to use only ASCII
* letters (upper or lower case), digits and the special characters '$', '_', '-', '.')</li>
* <li>the empty name segment is not supported</li>
* <li>names consisting of dots only, are reserved and must not be used for resources</li>
* <li>names are case sensitive although the underlying server might be case-insensitive</li>
* <li>the behavior with regard to URL encoded characters is not specified, %ddd notation should be avoided</li>
* <li>the meaning of a leading slash is undefined, but might be defined in future. It therefore should be avoided</li>
* </ul>
*
* UI5 APIs that only deal with Javascript resources, use a slight variation of this scheme,
* where the extension '.js' is always omitted (see {@link sap.ui.define}, {@link sap.ui.require}).
*
*
* <b>Relationship to old Module Name Syntax</b>
*
* Older UI5 APIs that deal with resources (like {@link jQuery.sap.registerModulePath},
* {@link jQuery.sap.require} and {@link jQuery.sap.declare}) used a dot-separated naming scheme
* (called 'module names') which was motivated by object names in the global namespace in
* Javascript.
*
* The new URN scheme better matches the names of the corresponding resources (files) as stored
* in a server and the dot ('.') is no longer a forbidden character in a resource name. This finally
* allows to handle resources with different types (extensions) with the same API, not only JS files.
*
* Last but not least does the URN scheme better match the naming conventions used by AMD loaders
* (like <code>requireJS</code>).
*
* @param {string} sResourceName unified resource name of the resource
* @returns {string} URL to load the resource from
* @public
* @experimental Since 1.27.0
* @function
*/
jQuery.sap.getResourcePath = getResourcePath;
/**
* Registers an URL prefix for a module name prefix.
*
* Before a module is loaded, the longest registered prefix of its module name
* is searched for and the associated URL prefix is used as a prefix for the request URL.
* The remainder of the module name is attached to the request URL by replacing
* dots ('.') with slashes ('/').
*
* The registration and search operates on full name segments only. So when a prefix
*
* 'sap.com' -> 'http://www.sap.com/ui5/resources/'
*
* is registered, then it will match the name
*
* 'sap.com.Button'
*
* but not
*
* 'sap.commons.Button'
*
* Note that the empty prefix ('') will always match and thus serves as a fallback for
* any search.
*
* The prefix can either be given as string or as object which contains the url and a 'final' property.
* If 'final' is set to true, overwriting a module prefix is not possible anymore.
*
* @param {string} sModuleName module name to register a path for
* @param {string | object} vUrlPrefix path prefix to register, either a string literal or an object (e.g. {url : 'url/to/res', 'final': true})
* @param {string} [vUrlPrefix.url] path prefix to register
* @param {boolean} [vUrlPrefix.final] flag to avoid overwriting the url path prefix for the given module name at a later point of time
*
* @public
* @static
* @SecSink {1|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.registerModulePath = function registerModulePath(sModuleName, vUrlPrefix) {
jQuery.sap.assert(!/\//.test(sModuleName), "module path must not contain a slash.");
sModuleName = sModuleName.replace(/\./g, "/");
// URL must not be empty
vUrlPrefix = vUrlPrefix || '.';
jQuery.sap.registerResourcePath(sModuleName, vUrlPrefix);
};
/**
* Registers an URL prefix for a resource name prefix.
*
* Before a resource is loaded, the longest registered prefix of its unified resource name
* is searched for and the associated URL prefix is used as a prefix for the request URL.
* The remainder of the resource name is attached to the request URL 1:1.
*
* The registration and search operates on full name segments only. So when a prefix
*
* 'sap/com' -> 'http://www.sap.com/ui5/resources/'
*
* is registered, then it will match the name
*
* 'sap/com/Button'
*
* but not
*
* 'sap/commons/Button'
*
* Note that the empty prefix ('') will always match and thus serves as a fallback for
* any search.
*
* The url prefix can either be given as string or as object which contains the url and a final flag.
* If final is set to true, overwriting a resource name prefix is not possible anymore.
*
* @param {string} sResourceNamePrefix in unified resource name syntax
* @param {string | object} vUrlPrefix prefix to use instead of the sResourceNamePrefix, either a string literal or an object (e.g. {url : 'url/to/res', 'final': true})
* @param {string} [vUrlPrefix.url] path prefix to register
* @param {boolean} [vUrlPrefix.final] flag to avoid overwriting the url path prefix for the given module name at a later point of time
*
* @public
* @static
* @SecSink {1|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.registerResourcePath = function registerResourcePath(sResourceNamePrefix, vUrlPrefix) {
sResourceNamePrefix = String(sResourceNamePrefix || "");
if (mUrlPrefixes[sResourceNamePrefix] && mUrlPrefixes[sResourceNamePrefix]["final"] == true) {
log.warning( "registerResourcePath with prefix " + sResourceNamePrefix + " already set as final to '" + mUrlPrefixes[sResourceNamePrefix].url + "'. This call is ignored." );
return;
}
if ( typeof vUrlPrefix === 'string' || vUrlPrefix instanceof String ) {
vUrlPrefix = { 'url' : vUrlPrefix };
}
if ( !vUrlPrefix || vUrlPrefix.url == null ) {
delete mUrlPrefixes[sResourceNamePrefix];
log.info("registerResourcePath ('" + sResourceNamePrefix + "') (registration removed)");
} else {
vUrlPrefix.url = String(vUrlPrefix.url);
// ensure that the prefix ends with a '/'
if ( vUrlPrefix.url.slice(-1) != '/' ) {
vUrlPrefix.url += '/';
}
mUrlPrefixes[sResourceNamePrefix] = vUrlPrefix;
log.info("registerResourcePath ('" + sResourceNamePrefix + "', '" + vUrlPrefix.url + "')" + ((vUrlPrefix['final']) ? " (final)" : ""));
}
};
/**
* Check whether a given module has been loaded / declared already.
*
* Returns true as soon as a module has been required the first time, even when
* loading/executing it has not finished yet. So the main assertion of a
* return value of <code>true</code> is that the necessary actions have been taken
* to make the module available in the near future. It does not mean, that
* the content of the module is already available!
*
* This fuzzy behavior is necessary to avoid multiple requests for the same module.
* As a consequence of the assertion above, a <i>preloaded</i> module does not
* count as <i>declared</i>. For preloaded modules, an explicit call to
* <code>jQuery.sap.require</code> is necessary to make them available.
*
* If a caller wants to know whether a module needs to be loaded from the server,
* it can set <code>bIncludePreloaded</code> to true. Then, preloaded modules will
* be reported as 'declared' as well by this method.
*
* @param {string} sModuleName name of the module to be checked
* @param {boolean} [bIncludePreloaded=false] whether preloaded modules should be reported as declared.
* @return {boolean} whether the module has been declared already
* @public
* @static
*/
jQuery.sap.isDeclared = function isDeclared(sModuleName, bIncludePreloaded) {
sModuleName = ui5ToRJS(sModuleName) + ".js";
return mModules[sModuleName] && (bIncludePreloaded || mModules[sModuleName].state !== PRELOADED);
};
/**
* Returns the names of all declared modules.
* @return {string[]} the names of all declared modules
* @see jQuery.sap.isDeclared
* @public
* @static
*/
jQuery.sap.getAllDeclaredModules = function() {
var aModules = [];
jQuery.each(mModules, function(sURN, oModule) {
// filter out preloaded modules
if ( oModule && oModule.state !== PRELOADED ) {
var sModuleName = urnToUI5(sURN);
if ( sModuleName ) {
aModules.push(sModuleName);
}
}
});
return aModules;
};
// take resource roots from configuration
if ( oCfgData.resourceroots ) {
jQuery.each(oCfgData.resourceroots, jQuery.sap.registerModulePath);
}
// dump the URL prefixes
log.info("URL prefixes set to:");
for (var n in mUrlPrefixes) {
log.info(" " + (n ? "'" + n + "'" : "(default)") + " : " + mUrlPrefixes[n].url + ((mUrlPrefixes[n]['final']) ? " (final)" : "") );
}
/**
* Declares a module as existing.
*
* By default, this function assumes that the module will create a JavaScript object
* with the same name as the module. As a convenience it ensures that the parent
* namespace for that object exists (by calling jQuery.sap.getObject).
* If such an object creation is not desired, <code>bCreateNamespace</code> must be set to false.
*
* @param {string | object} sModuleName name of the module to be declared
* or in case of an object {modName: "...", type: "..."}
* where modName is the name of the module and the type
* could be a specific dot separated extension e.g.
* <code>{modName: "sap.ui.core.Dev", type: "view"}</code>
* loads <code>sap/ui/core/Dev.view.js</code> and
* registers as <code>sap.ui.core.Dev.view</code>
* @param {boolean} [bCreateNamespace=true] whether to create the parent namespace
*
* @public
* @static
*/
jQuery.sap.declare = function(sModuleName, bCreateNamespace) {
var sNamespaceObj = sModuleName;
// check for an object as parameter for sModuleName
// in case of this the object contains the module name and the type
// which could be {modName: "sap.ui.core.Dev", type: "view"}
if (typeof (sModuleName) === "object") {
sNamespaceObj = sModuleName.modName;
sModuleName = ui5ToRJS(sModuleName.modName) + (sModuleName.type ? "." + sModuleName.type : "") + ".js";
} else {
sModuleName = ui5ToRJS(sModuleName) + ".js";
}
declareModule(sModuleName);
// ensure parent namespace even if module was declared already
// (as declare might have been called by require)
if (bCreateNamespace !== false) {
// ensure parent namespace
jQuery.sap.getObject(sNamespaceObj, 1);
}
return this;
};
/**
* Ensures that the given module is loaded and executed before execution of the
* current script continues.
*
* By issuing a call to this method, the caller declares a dependency to the listed modules.
*
* Any required and not yet loaded script will be loaded and execute synchronously.
* Already loaded modules will be skipped.
*
* @param {...string | object} vModuleName one or more names of modules to be loaded
* or in case of an object {modName: "...", type: "..."}
* where modName is the name of the module and the type
* could be a specific dot separated extension e.g.
* <code>{modName: "sap.ui.core.Dev", type: "view"}</code>
* loads <code>sap/ui/core/Dev.view.js</code> and
* registers as <code>sap.ui.core.Dev.view</code>
*
* @public
* @static
* @function
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.require = function(vModuleName, fnCallback) {
if ( arguments.length > 1 ) {
// legacy mode with multiple arguments, each representing a dependency
for (var i = 0; i < arguments.length; i++) {
jQuery.sap.require(arguments[i]);
}
return this;
}
// check for an object as parameter for sModuleName
// in case of this the object contains the module name and the type
// which could be {modName: "sap.ui.core.Dev", type: "view"}
if (typeof (vModuleName) === "object") {
jQuery.sap.assert(!vModuleName.type || jQuery.inArray(vModuleName.type, mKnownSubtypes.js) >= 0, "type must be empty or one of " + mKnownSubtypes.js.join(", "));
vModuleName = ui5ToRJS(vModuleName.modName) + (vModuleName.type ? "." + vModuleName.type : "") + ".js";
} else {
vModuleName = ui5ToRJS(vModuleName) + ".js";
}
requireModule(vModuleName);
return this; // TODO
};
/**
* UI5 internal method that loads the given module, specified in requireJS notation (URL like, without extension).
*
* Applications MUST NOT USE THIS METHOD as it will be removed in one of the future versions.
* It is only intended for sap.ui.component.
*
* @param {string} sModuleName Module name in requireJS syntax
* @private
*/
jQuery.sap._requirePath = function(sModuleName) {
requireModule(sModuleName + ".js");
};
window.sap = window.sap || {};
sap.ui = sap.ui || {};
/**
* Defines a Javascript module with its name, its dependencies and a module value or factory.
*
* The typical and only suggested usage of this method is to have one single, top level call to
* <code>sap.ui.define</code> in one Javascript resource (file). When a module is requested by its
* name for the first time, the corresponding resource is determined from the name and the current
* {@link jQuery.sap.registerResourcePath configuration}. The resource will be loaded and executed
* which in turn will execute the top level <code>sap.ui.define</code> call.
*
* If the module name was omitted from that call, it will be substituted by the name that was used to
* request the module. As a preparation step, the dependencies as well as their transitive dependencies,
* will be loaded. Then, the module value will be determined: if a static value (object, literal) was
* given, that value will be the module value. If a function was given, that function will be called
* (providing the module values of the declared dependencies as parameters to the function) and its
* return value will be used as module value. The framework internally associates the resulting value
* with the module name and provides it to the original requestor of the module. Whenever the module
* is requested again, the same value will be returned (modules are executed only once).
*
* <i>Example:</i><br>
* The following example defines a module "SomeClass", but doesn't hard code the module name.
* If stored in a file 'sap/mylib/SomeClass.js', it can be requested as 'sap/mylib/SomeClass'.
* <pre>
* sap.ui.define(['./Helper', 'sap/m/Bar'], function(Helper,Bar) {
*
* // create a new class
* var SomeClass = function();
*
* // add methods to its prototype
* SomeClass.prototype.foo = function() {
*
* // use a function from the dependency 'Helper' in the same package (e.g. 'sap/mylib/Helper' )
* var mSettings = Helper.foo();
*
* // create and return a sap.m.Bar (using its local name 'Bar')
* return new Bar(mSettings);
*
* }
*
* // return the class as module value
* return SomeClass;
*
* });
* </pre>
*
* In another module or in an application HTML page, the {@link sap.ui.require} API can be used
* to load the Something module and to work with it:
*
* <pre>
* sap.ui.require(['sap/mylib/Something'], function(Something) {
*
* // instantiate a Something and call foo() on it
* new Something().foo();
*
* });
* </pre>
*
* <b>Module Name Syntax</b><br>
* <code>sap.ui.define</code> uses a simplified variant of the {@link jQuery.sap.getResourcePath
* unified resource name} syntax for the module's own name as well as for its dependencies.
* The only difference to that syntax is, that for <code>sap.ui.define</code> and
* <code>sap.ui.require</code>, the extension (which always would be '.js') has to be omitted.
* Both methods always add this extension internally.
*
* As a convenience, the name of a dependency can start with the segment './' which will be
* replaced by the name of the package that contains the currently defined module (relative name).
*
* It is best practice to omit the name of the defined module (first parameter) and to use
* relative names for the dependencies whenever possible. This reduces the necessary configuration,
* simplifies renaming of packages and allows to map them to a different namespace.
*
*
* <b>Dependency to Modules</b><br>
* If a dependencies array is given, each entry represents the name of another module that
* the currently defined module depends on. All dependency modules are loaded before the value
* of the currently defined module is determined. The module value of each dependency module
* will be provided as a parameter to a factory function, the order of the parameters will match
* the order of the modules in the dependencies array.
*
* <b>Note:</b> the order in which the dependency modules are <i>executed</i> is <b>not</b>
* defined by the order in the dependencies array! The execution order is affected by dependencies
* <i>between</i> the dependency modules as well as by their current state (whether a module
* already has been loaded or not). Neither module implementations nor dependants that require
* a module set must make any assumption about the execution order (other than expressed by
* their dependencies). There is, however, one exception with regard to third party libraries,
* see the list of limitations further down below.
*
* <b>Note:</b>a static module value (a literal provided to <code>sap.ui.define</code>) cannot
* depend on the module values of the depency modules. Instead, modules can use a factory function,
* calculate the static value in that function, potentially based on the dependencies, and return
* the result as module value. The same approach must be taken when the module value is supposed
* to be a function.
*
*
* <b>Asynchronous Contract</b><br>
* <code>sap.ui.define</code> is designed to support real Asynchronous Module Definitions (AMD)
* in future, although it internally still uses the the old synchronous module loading of UI5.
* Callers of <code>sap.ui.define</code> therefore must not rely on any synchronous behavior
* that they might observe with the current implementation.
*
* For example, callers of <code>sap.ui.define</code> must not use the module value immediately
* after invoking <code>sap.ui.define</code>:
*
* <pre>
* // COUNTER EXAMPLE HOW __NOT__ TO DO IT
*
* // define a class Something as AMD module
* sap.ui.define('Something', [], function() {
* var Something = function();
* return Something;
* });
*
* // DON'T DO THAT!
* // accessing the class _synchronously_ after sap.ui.define was called
* new Something();
* </pre>
*
* Applications that need to ensure synchronous module definition or synchronous loading of dependencies
* <b>MUST</b> use the old {@link jQuery.sap.declare} and {@link jQuery.sap.require} APIs.
*
*
* <b>(No) Global References</b><br>
* To be in line with AMD best practices, modules defined with <code>sap.ui.define</code>
* should not make any use of global variables if those variables are also available as module
* values. Instead, they should add dependencies to those modules and use the corresponding parameter
* of the factory function to access the module value.
*
* As the current programming model and the documentation of UI5 heavily rely on global names,
* there will be a transition phase where UI5 enables AMD modules and local references to module
* values in parallel to the old global names. The fourth parameter of <code>sap.ui.define</code>
* has been added to support that transition phase. When this parameter is set to true, the framework
* provides two additional functionalities
*
* <ol>
* <li>before the factory function is called, the existence of the global parent namespace for
* the current module is ensured</li>
* <li>the module value will be automatically exported under a global name which is derived from
* the name of the module</li>
* </ol>
*
* The parameter lets the framework know whether any of those two operations is needed or not.
* In future versions of UI5, a central configuration option is planned to suppress those 'exports'.
*
*
* <b>Third Party Modules</b><br>
* Although third party modules don't use UI5 APIs, they still can be listed as dependencies in
* a <code>sap.ui.define</code> call. They will be requested and executed like UI5 modules, but their
* module value will be <code>undefined</code>.
*
* If the currently defined module needs to access the module value of such a third party module,
* it can access the value via its global name (if the module supports such a usage).
*
* Note that UI5 temporarily deactivates an existing AMD loader while it executes third party modules
* known to support AMD. This sounds contradictarily at a first glance as UI5 wants to support AMD,
* but for now it is necessary to fully support UI5 apps that rely on global names for such modules.
*
* Example:
* <pre>
* // module 'Something' wants to use third party library 'URI.js'
* // It is packaged by UI5 as non-UI5-module 'sap/ui/thirdparty/URI'
*
* sap.ui.define('Something', ['sap/ui/thirdparty/URI'], function(URIModuleValue) {
*
* new URIModuleValue(); // fails as module value is undefined
*
* //global URI // (optional) declare usage of global name so that static code checks don't complain
* new URI(); // access to global name 'URI' works
*
* ...
* });
* </pre>
*
*
* <b>Differences to requireJS</b><br>
* The current implementation of <code>sap.ui.define</code> differs from <code>requireJS</code>
* or other AMD loaders in several aspects:
* <ul>
* <li>the name <code>sap.ui.define</code> is different from the plain <code>define</code>.
* This has two reasons: first, it avoids the impression that <code>sap.ui.define</code> is
* an exact implementation of an AMD loader. And second, it allows the coexistence of an AMD
* loader (requireJS) and <code>sap.ui.define</code> in one application as long as UI5 or
* apps using UI5 are not fully prepared to run with an AMD loader</li>
* <li><code>sap.ui.define</code> currently loads modules with synchronous XHR calls. This is
* basically a tribute to the synchronous history of UI5.
* <b>BUT:</b> synchronous dependency loading and factory execution explicitly it not part of
* contract of <code>sap.ui.define</code>. To the contrary, it is already clear and planned
* that asynchronous loading will be implemented, at least as an alternative if not as the only
* implementation. Also check section <b>Asynchronous Contract</b> above.<br>
* Applications that need to ensure synchronous loading of dependencies <b>MUST</b> use the old
* {@link jQuery.sap.require} API.</li>
* <li><code>sap.ui.define</code> does not support plugins to use other file types, formats or
* protocols. It is not planned to support this in future</li>
* <li><code>sap.ui.define</code> does <b>not</b> support the 'sugar' of requireJS where CommonJS
* style dependency declarations using <code>sap.ui.require("something")</code> are automagically
* converted into <code>sap.ui.define</code> dependencies before executing the factory function.</li>
* <li><code>sap.ui.define</code> does not support the '../' prefix for module names. Only
* relative names in the same package or in subpackages thereof are supported.</li>
* </ul>
*
*
* <b>Limitations, Design Considerations</b><br>
* <ul>
* <li><b>Limitation</b>: as dependency management is not supported for Non-UI5 modules, the only way
* to ensure proper execution order for such modules currently is to rely on the order in the
* dependency array. Obviously, this only works as long as <code>sap.ui.define</code> uses
* synchronous loading. It will be enhanced when asynchronous loading is implemented.</li>
* <li>it was discussed to enfore asynchronous execution of the module factory function (e.g. with a
* timeout of 0). But this would have invalidated the current migration scenario where a
* sync <code>jQuery.sap.require</code> call can load a <code>sap.ui.define</code>'ed module.
* If the module definition would not execute synchronously, the synchronous contract of the
* require call would be broken (default behavior in existing UI5 apps)</li>
* <li>a single file must not contain multiple calls to <code>sap.ui.define</code>. Multiple calls
* currently are only supported in the so called 'preload' files that the UI5 merge tooling produces.
* The exact details of how this works might be changed in future implementations and are not
* yet part of the API contract</li>
* </ul>
* @param {string} [sModuleName] name of the module in simplified resource name syntax.
* When omitted, the loader determines the name from the request.
* @param {string[]} [aDependencies] list of dependencies of the module
* @param {function|any} vFactory the module value or a function that calculates the value
* @param {boolean} [bExport] whether an export to global names is required - should be used by SAP-owned code only
* @since 1.27.0
* @public
* @experimental Since 1.27.0 - not all aspects of sap.ui.define are settled yet. If the documented
* constraints and limitations are obeyed, SAP-owned code might use it. If the fourth parameter
* is not used and if the asynchronous contract is respected, even Non-SAP code might use it.
*/
sap.ui.define = function(sModuleName, aDependencies, vFactory, bExport) {
var sResourceName, i;
// optional id
if ( typeof sModuleName === 'string' ) {
sResourceName = sModuleName + '.js';
} else {
// shift parameters
bExport = vFactory;
vFactory = aDependencies;
aDependencies = sModuleName;
sResourceName = _execStack[_execStack.length - 1];
}
// convert module name to UI5 module name syntax (might fail!)
sModuleName = urnToUI5(sResourceName);
// optional array of dependencies
if ( !jQuery.isArray(aDependencies) ) {
// shift parameters
bExport = vFactory;
vFactory = aDependencies;
aDependencies = [];
} else {
// resolve relative module names
var sPackage = sResourceName.slice(0,1 + sResourceName.lastIndexOf('/'));
for (i = 0; i < aDependencies.length; i++) {
if ( /^\.\//.test(aDependencies[i]) ) {
aDependencies[i] = sPackage + aDependencies[i].slice(2); // 2 == length of './' prefix
}
}
}
if ( log.isLoggable() ) {
log.debug("define(" + sResourceName + ", " + "['" + aDependencies.join("','") + "']" + ")");
}
var oModule = declareModule(sResourceName);
// note: dependencies will be converted from RJS to URN inside requireAll
requireAll(aDependencies, function(aModules) {
// factory
if ( log.isLoggable() ) {
log.debug("define(" + sResourceName + "): calling factory " + typeof vFactory);
}
if ( bExport ) {
// ensure parent namespace
jQuery.sap.getObject(sModuleName, 1);
}
if ( typeof vFactory === 'function' ) {
oModule.content = vFactory.apply(window, aModules);
} else {
oModule.content = vFactory;
}
// HACK: global export
if ( bExport ) {
if ( oModule.content == null ) {
log.error("module '" + sResourceName + "' returned no content, but should be exported");
} else {
if ( log.isLoggable() ) {
log.debug("exporting content of '" + sResourceName + "': as global object");
}
jQuery.sap.setObject(sModuleName, oModule.content);
}
}
});
};
/**
* Resolves one or more module dependencies.
*
* <b>Synchronous Retrieval of a Single Module Value</b>
*
* When called with a single string, that string is assumed to be the name of an already loaded
* module and the value of that module is returned. If the module has not been loaded yet,
* or if it is a Non-UI5 module (e.g. third party module), <code>undefined</code> is returned.
* This signature variant allows synchronous access to module values without initiating module loading.
*
* Sample:
* <pre>
* var JSONModel = sap.ui.require("sap/ui/model/json/JSONModel");
* </pre>
*
* For modules that are known to be UI5 modules, this signature variant can be used to check whether
* the module has been loaded.
*
* <b>Asynchronous Loading of Multiple Modules</b>
*
* If an array of strings is given and (optionally) a callback function, then the strings
* are interpreted as module names and the corresponding modules (and their transitive
* dependencies) are loaded. Then the callback function will be called asynchronously.
* The module values of the specified modules will be provided as parameters to the callback
* function in the same order in which they appeared in the dependencies array.
*
* The return value for the asynchronous use case is <code>undefined</code>.
*
* <pre>
* sap.ui.require(['sap/ui/model/json/JSONModel', 'sap/ui/core/UIComponent'], function(JSONModel,UIComponent) {
*
* var MyComponent = UIComponent.extend('MyComponent', {
* ...
* });
* ...
*
* });
* </pre>
*
* This method uses the same variation of the {@link jQuery.sap.getResourcePath unified resource name}
* syntax that {@link sap.ui.define} uses: module names are specified without the implicit extension '.js'.
* Relative module names are not supported.
*
* @param {string|string[]} vDependencies dependency (dependencies) to resolve
* @param {function} [fnCallback] callback function to execute after resolving an array of dependencies
* @returns {any|undefined} a single module value or undefined
* @public
* @experimental Since 1.27.0 - not all aspects of sap.ui.require are settled yet. E.g. the return value
* of the asynchronous use case might change (currently it is undefined).
*/
sap.ui.require = function(vDependencies, fnCallback) {
jQuery.sap.assert(typeof vDependencies === 'string' || jQuery.isArray(vDependencies), "dependency param either must be a single string or an array of strings");
jQuery.sap.assert(fnCallback == null || typeof fnCallback === 'function', "callback must be a function or null/undefined");
if ( typeof vDependencies === 'string' ) {
var sModuleName = vDependencies + '.js',
oModule = mModules[sModuleName];
return oModule ? (oModule.content || jQuery.sap.getObject(urnToUI5(sModuleName))) : undefined;
}
requireAll(vDependencies, function(aModules) {
if ( typeof fnCallback === 'function' ) {
// enforce asynchronous execution of callback
setTimeout(function() {
fnCallback.apply(window, aModules);
},0);
}
});
// return undefined;
};
jQuery.sap.preloadModules = function(sPreloadModule, bAsync, oSyncPoint) {
var sURL, iTask;
jQuery.sap.assert(!bAsync || oSyncPoint, "if mode is async, a syncpoint object must be given");
if ( mPreloadModules[sPreloadModule] ) {
return;
}
mPreloadModules[sPreloadModule] = true;
sURL = jQuery.sap.getModulePath(sPreloadModule, ".json");
log.debug("preload file " + sPreloadModule);
iTask = oSyncPoint && oSyncPoint.startTask("load " + sPreloadModule);
jQuery.ajax({
dataType : "json",
async : bAsync,
url : sURL,
success : function(data) {
if ( data ) {
data.url = sURL;
}
jQuery.sap.registerPreloadedModules(data, bAsync, oSyncPoint);
oSyncPoint && oSyncPoint.finishTask(iTask);
},
error : function(xhr, textStatus, error) {
log.error("failed to preload '" + sPreloadModule + "': " + (error || textStatus));
oSyncPoint && oSyncPoint.finishTask(iTask, false);
}
});
};
jQuery.sap.registerPreloadedModules = function(oData, bAsync, oSyncPoint) {
var bOldSyntax = Version(oData.version || "1.0").compareTo("2.0") < 0;
if ( log.isLoggable() ) {
log.debug(sLogPrefix + "adding preloaded modules from '" + oData.url + "'");
}
if ( oData.name ) {
mPreloadModules[oData.name] = true;
}
jQuery.each(oData.modules, function(sName,sContent) {
sName = bOldSyntax ? ui5ToRJS(sName) + ".js" : sName;
if ( !mModules[sName] ) {
mModules[sName] = { state : PRELOADED, url : oData.url + "/" + sName, data : sContent, group: oData.name };
}
// when a library file is preloaded, also mark its preload file as loaded
// for normal library preload, this is redundant, but for non-default merged entities
// like sap/fiori/core.js it avoids redundant loading of library preload files
if ( sName.match(/\/library\.js$/) ) {
mPreloadModules[urnToUI5(sName) + "-preload"] = true;
}
});
if ( oData.dependencies ) {
jQuery.each(oData.dependencies, function(idx,sModuleName) {
jQuery.sap.preloadModules(sModuleName, bAsync, oSyncPoint);
});
}
};
/**
* Removes a set of resources from the resource cache.
*
* @param {string} sName unified resource name of a resource or the name of a preload group to be removed
* @param {boolean} [bPreloadGroup=true] whether the name specifies a preload group, defaults to true
* @param {boolean} [bUnloadAll] Whether all matching resources should be unloaded, even if they have been executed already.
* @param {boolean} [bDeleteExports] Whether exportss (global variables) should be destroyed as well. Will be done for UI5 module names only.
* @experimental Since 1.16.3 API might change completely, apps must not develop against it.
* @private
*/
jQuery.sap.unloadResources = function(sName, bPreloadGroup, bUnloadAll, bDeleteExports) {
var aModules = [];
if ( bPreloadGroup == null ) {
bPreloadGroup = true;
}
if ( bPreloadGroup ) {
// collect modules that belong to the given group
jQuery.each(mModules, function(sURN, oModule) {
if ( oModule && oModule.group === sName ) {
aModules.push(sURN);
}
});
// also remove a preload entry
delete mPreloadModules[sName];
} else {
// single module
if ( mModules[sName] ) {
aModules.push(sName);
}
}
jQuery.each(aModules, function(i, sURN) {
var oModule = mModules[sURN];
if ( oModule && bDeleteExports && sURN.match(/\.js$/) ) {
jQuery.sap.setObject(urnToUI5(sURN), undefined); // TODO really delete property
}
if ( oModule && (bUnloadAll || oModule.state === PRELOADED) ) {
delete mModules[sURN];
}
});
};
/**
* Converts a UI5 module name to a unified resource name.
*
* Used by View and Fragment APIs to convert a given module name into an URN.
*
* @experimental Since 1.16.0, not for public usage yet.
* @private
*/
jQuery.sap.getResourceName = function(sModuleName, sSuffix) {
return ui5ToRJS(sModuleName) + (sSuffix || ".js");
};
/**
* Retrieves the resource with the given name, either from the preload cache or from
* the server. The expected data type of the resource can either be specified in the
* options (<code>dataType</code>) or it will be derived from the suffix of the <code>sResourceName</code>.
* The only supported data types so far are xml, html, json and text. If the resource name extension
* doesn't match any of these extensions, the data type must be specified in the options.
*
* If the resource is found in the preload cache, it will be converted from text format
* to the requested <code>dataType</code> using a converter from <code>jQuery.ajaxSettings.converters</code>.
*
* If it is not found, the resource name will be converted to a resource URL (using {@link #getResourcePath})
* and the resulting URL will be requested from the server with a synchronous jQuery.ajax call.
*
* If the resource was found in the local preload cache and any necessary conversion succeeded
* or when the resource was retrieved from the backend successfully, the content of the resource will
* be returned. In any other case, an exception will be thrown, or if option failOnError is set to true,
* <code>null</code> will be returned.
*
* Future implementations of this API might add more options. Generic implementations that accept an
* <code>mOptions</code> object and propagate it to this function should limit the options to the currently
* defined set of options or they might fail for unknown options.
*
* For asynchronous calls the return value of this method is an ECMA Script 6 Promise object which callbacks are triggered
* when the resource is ready:
* If <code>failOnError</code> is <code>false</code> the catch callback of the promise is not called. The argument given to the fullfilled
* callback is null in error case.
* If <code>failOnError</code> is <code>true</code> the catch callback will be triggered. The argument is an Error object in this case.
*
* @param {string} [sResourceName] resourceName in unified resource name syntax
* @param {object} [mOptions] options
* @param {object} [mOptions.dataType] one of "xml", "html", "json" or "text". If not specified it will be derived from the resource name (extension)
* @param {string} [mOptions.name] unified resource name of the resource to load (alternative syntax)
* @param {string} [mOptions.url] url of a resource to load (alternative syntax, name will only be a guess)
* @param {string} [mOptions.headers] Http headers for an eventual XHR request
* @param {string} [mOptions.failOnError=true] whether to propagate load errors or not
* @param {string} [mOptions.async=false] whether the loading should be performed asynchronously.
* @return {string|Document|object|Promise} content of the resource. A string for text or html, an Object for JSON, a Document for XML. For asynchronous calls an ECMA Script 6 Promise object will be returned.
* @throws Error if loading the resource failed
* @private
* @experimental API is not yet fully mature and may change in future.
* @since 1.15.1
*/
jQuery.sap.loadResource = function(sResourceName, mOptions) {
var sType,
oData,
sUrl,
oError,
oDeferred;
if ( typeof sResourceName === "string" ) {
mOptions = mOptions || {};
} else {
mOptions = sResourceName || {};
sResourceName = mOptions.name;
if ( !sResourceName && mOptions.url) {
sResourceName = guessResourceName(mOptions.url);
}
}
// defaulting
mOptions = jQuery.extend({ failOnError: true, async: false }, mOptions);
sType = mOptions.dataType;
if ( sType == null && sResourceName ) {
sType = (sType = rTypes.exec(sResourceName)) && sType[1];
}
jQuery.sap.assert(/^(xml|html|json|text)$/.test(sType), "type must be one of xml, html, json or text");
oDeferred = mOptions.async ? new jQuery.Deferred() : null;
function handleData(d, e) {
if ( d == null && mOptions.failOnError ) {
e = e || new Error("no data returned for " + sResourceName);
if (mOptions.async) {
oDeferred.reject(e);
jQuery.sap.log.error(e);
return d;
}
throw e;
}
if (mOptions.async) {
oDeferred.resolve(d);
}
return d;
}
function convertData(d) {
var vConverter = jQuery.ajaxSettings.converters["text " + sType];
if ( typeof vConverter === "function" ) {
d = vConverter(d);
}
return handleData(d);
}
if ( sResourceName && mModules[sResourceName] ) {
oData = mModules[sResourceName].data;
mModules[sResourceName].state = LOADED;
}
if ( oData != null ) {
if (mOptions.async) {
//Use timeout to simulate async behavior for this sync case for easier usage
setTimeout(function(){
convertData(oData);
}, 0);
} else {
oData = convertData(oData);
}
} else {
jQuery.ajax({
url : sUrl = mOptions.url || getResourcePath(sResourceName),
async : mOptions.async,
dataType : sType,
headers: mOptions.headers,
success : function(data, textStatus, xhr) {
oData = handleData(data);
},
error : function(xhr, textStatus, error) {
oError = new Error("resource " + sResourceName + " could not be loaded from " + sUrl + ". Check for 'file not found' or parse errors. Reason: " + error);
oError.status = textStatus;
oError.error = error;
oError.statusCode = xhr.status;
oData = handleData(null, oError);
}
});
}
return mOptions.async ? window.Promise.resolve(oDeferred) : oData;
};
/*
* register a global event handler to detect script execution errors.
* Only works for browsers that support document.currentScript.
* /
window.addEventListener("error", function(e) {
if ( document.currentScript && document.currentScript.dataset.sapUiModule ) {
var error = {
message: e.message,
filename: e.filename,
lineno: e.lineno,
colno: e.colno
};
document.currentScript.dataset.sapUiModuleError = JSON.stringify(error);
}
});
*/
/**
* Loads the given Javascript resource (URN) asynchronously via as script tag.
* Returns a promise that will be resolved when the load event is fired or reject
* when the error event is fired.
*
* Note: execution errors of the script are not reported as 'error'.
*
* This method is not a full implementation of require. It is intended only for
* loading "preload" files that do not define an own module / module value.
*
* Functionality might be removed/renamed in future, so no code outside the
* sap.ui.core library must use it.
*
* @experimental
* @private
*/
jQuery.sap._loadJSResourceAsync = function(sResource, bIgnoreErrors) {
return new Promise(function(resolve,reject) {
var oModule = mModules[sResource] || (mModules[sResource] = { state : INITIAL });
var sUrl = oModule.url = getResourcePath(sResource);
oModule.state = LOADING;
var oScript = window.document.createElement('SCRIPT');
oScript.src = sUrl;
oScript.dataset.sapUiModule = sResource;
oScript.dataset.sapUiModuleError = '';
oScript.addEventListener('load', function(e) {
jQuery.sap.log.info("Javascript resource loaded: " + sResource);
// TODO either find a cross-browser solution to detect and assign execution errros or document behavior
// var error = e.target.dataset.sapUiModuleError;
// if ( error ) {
// oModule.state = FAILED;
// oModule.error = JSON.parse(error);
// jQuery.sap.log.error("failed to load Javascript resource: " + sResource + ":" + error);
// reject(oModule.error);
// }
oModule.state = READY;
// TODO oModule.data = ?
resolve();
});
oScript.addEventListener('error', function(e) {
jQuery.sap.log.error("failed to load Javascript resource: " + sResource);
oModule.state = FAILED;
// TODO oModule.error = xhr ? xhr.status + " - " + xhr.statusText : textStatus;
if ( bIgnoreErrors ) {
resolve();
} else {
reject();
}
});
appendHead(oScript);
});
};
return function() {
//remove final information in mUrlPrefixes
var mFlatUrlPrefixes = {};
jQuery.each(mUrlPrefixes, function(sKey,oUrlPrefix) {
mFlatUrlPrefixes[sKey] = oUrlPrefix.url;
});
return { modules : mModules, prefixes : mFlatUrlPrefixes };
};
}());
// --------------------- script and stylesheet handling --------------------------------------------------
// appends a link object to the head
function appendHead(oElement) {
var head = window.document.getElementsByTagName("head")[0];
if (head) {
head.appendChild(oElement);
}
}
/**
* Includes the script (via &lt;script&gt;-tag) into the head for the
* specified <code>sUrl</code> and optional <code>sId</code>.
* <br>
* <i>In case of IE8 only the load callback will work ignoring in case of success and error.</i>
*
* @param {string}
* sUrl the URL of the script to load
* @param {string}
* [sId] id that should be used for the script include tag
* @param {function}
* [fnLoadCallback] callback function to get notified once the script has been loaded
* @param {function}
* [fnErrorCallback] callback function to get notified once the script loading failed (not supported by IE8)
*
* @public
* @static
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.includeScript = function includeScript(sUrl, sId, fnLoadCallback, fnErrorCallback){
var oScript = window.document.createElement("script");
oScript.src = sUrl;
oScript.type = "text/javascript";
if (sId) {
oScript.id = sId;
}
if (!!sap.ui.Device.browser.internet_explorer && sap.ui.Device.browser.version < 9) {
// in case if IE8 the error callback is not supported!
// we can only check the loading via the readystatechange event
if (fnLoadCallback) {
oScript.onreadystatechange = function() {
if (oScript.readyState === "loaded" || oScript.readyState === "complete") {
fnLoadCallback();
oScript.onreadystatechange = null;
}
};
}
} else {
if (fnLoadCallback) {
jQuery(oScript).load(fnLoadCallback);
}
if (fnErrorCallback) {
jQuery(oScript).error(fnErrorCallback);
}
}
// jQuery("head").append(oScript) doesn't work because they filter for the script
// and execute them directly instead adding the SCRIPT tag to the head
var oOld;
if ((sId && (oOld = jQuery.sap.domById(sId)) && oOld.tagName === "SCRIPT")) {
jQuery(oOld).remove(); // replacing scripts will not trigger the load event
}
appendHead(oScript);
};
var oIEStyleSheetNode;
var mIEStyleSheets = jQuery.sap._mIEStyleSheets = {};
/**
* Includes the specified stylesheet via a &lt;link&gt;-tag in the head of the current document. If there is call to
* <code>includeStylesheet</code> providing the sId of an already included stylesheet, the existing element will be
* replaced.
*
* @param {string}
* sUrl the URL of the script to load
* @param {string}
* [sId] id that should be used for the script include tag
* @param {function}
* [fnLoadCallback] callback function to get notified once the link has been loaded
* @param {function}
* [fnErrorCallback] callback function to get notified once the link loading failed.
* In case of usage in IE the error callback will also be executed if an empty stylesheet
* is loaded. This is the only option how to determine in IE if the load was successful
* or not since the native onerror callback for link elements doesn't work in IE. The IE
* always calls the onload callback of the link element.
*
* @public
* @static
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.includeStyleSheet = function includeStyleSheet(sUrl, sId, fnLoadCallback, fnErrorCallback) {
var _createLink = function(sUrl, sId, fnLoadCallback, fnErrorCallback){
// create the new link element
var oLink = document.createElement("link");
oLink.type = "text/css";
oLink.rel = "stylesheet";
oLink.href = sUrl;
if (sId) {
oLink.id = sId;
}
var fnError = function() {
jQuery(oLink).attr("sap-ui-ready", "false");
if (fnErrorCallback) {
fnErrorCallback();
}
};
var fnLoad = function() {
jQuery(oLink).attr("sap-ui-ready", "true");
if (fnLoadCallback) {
fnLoadCallback();
}
};
// for IE we will check if the stylesheet contains any rule and then
// either trigger the load callback or the error callback
if (!!sap.ui.Device.browser.internet_explorer) {
var fnLoadOrg = fnLoad;
fnLoad = function(oEvent) {
var aRules;
try {
// in cross-origin scenarios the IE can still access the rules of the stylesheet
// if the stylesheet has been loaded properly
aRules = oEvent.target && oEvent.target.sheet && oEvent.target.sheet.rules;
// in cross-origin scenarios now the catch block will be executed because we
// cannot access the rules of the stylesheet but for non cross-origin stylesheets
// we will get an empty rules array and finally we cannot differ between
// empty stylesheet or loading issue correctly => documented in JSDoc!
} catch (ex) {
// exception happens when the stylesheet could not be loaded from the server
// we now ignore this and know that the stylesheet doesn't exists => trigger error
}
// no rules means error
if (aRules && aRules.length > 0) {
fnLoadOrg();
} else {
fnError();
}
};
}
jQuery(oLink).load(fnLoad);
jQuery(oLink).error(fnError);
return oLink;
};
var _appendStyle = function(sUrl, sId, fnLoadCallback, fnErrorCallback){
if (sap.ui.Device.browser.internet_explorer && sap.ui.Device.browser.version <= 9 && document.styleSheets.length >= 28) {
// in IE9 only 30 links are alowed, so use stylesheet object insted
var sRootUrl = URI.parse(document.URL).path;
var sAbsoluteUrl = new URI(sUrl).absoluteTo(sRootUrl).toString();
if (sId) {
var oIEStyleSheet = mIEStyleSheets[sId];
if (oIEStyleSheet && oIEStyleSheet.href === sAbsoluteUrl) {
// if stylesheet was already included and href is the same, do nothing
return;
}
}
jQuery.sap.log.warning("Stylesheet " + (sId ? sId + " " : "") + "not added as LINK because of IE limits", sUrl, "jQuery.sap.includeStyleSheet");
if (!oIEStyleSheetNode) {
// create a style sheet to add additional style sheet. But for this the Replace logic will not work any more
// the callback functions are not used in this case
// the sap-ui-ready attribute will not be set -> maybe problems with ThemeCheck
oIEStyleSheetNode = document.createStyleSheet();
}
// add up to 30 style sheets to every of this style sheets. (result is a tree of style sheets)
var bAdded = false;
for ( var i = 0; i < oIEStyleSheetNode.imports.length; i++) {
var oStyleSheet = oIEStyleSheetNode.imports[i];
if (oStyleSheet.imports.length < 30) {
oStyleSheet.addImport(sAbsoluteUrl);
bAdded = true;
break;
}
}
if (!bAdded) {
oIEStyleSheetNode.addImport(sAbsoluteUrl);
}
if (sId) {
// remember id and href URL in internal map as there is no link tag that can be checked
mIEStyleSheets[sId] = {
href: sAbsoluteUrl
};
}
// always make sure to re-append the customcss in the end if it exists
var oCustomCss = document.getElementById('sap-ui-core-customcss');
if (!jQuery.isEmptyObject(oCustomCss)) {
appendHead(oCustomCss);
}
} else {
var oLink = _createLink(sUrl, sId, fnLoadCallback, fnErrorCallback);
if (jQuery('#sap-ui-core-customcss').length > 0) {
jQuery('#sap-ui-core-customcss').first().before(jQuery(oLink));
} else {
appendHead(oLink);
}
}
};
// check for existence of the link
var oOld = jQuery.sap.domById(sId);
if (oOld && oOld.tagName === "LINK" && oOld.rel === "stylesheet") {
// link exists, so we replace it - but only if a callback has to be attached or if the href will change. Otherwise don't touch it
if (fnLoadCallback || fnErrorCallback || oOld.href !== URI(String(sUrl), URI().search("") /* returns current URL without search params */ ).toString()) {
jQuery(oOld).replaceWith(_createLink(sUrl, sId, fnLoadCallback, fnErrorCallback));
}
} else {
_appendStyle(sUrl, sId, fnLoadCallback, fnErrorCallback);
}
};
// TODO should be in core, but then the 'callback' could not be implemented
if ( !(oCfgData.productive === true || oCfgData.productive === "true" || oCfgData.productive === "x") ) {
jQuery(function() {
jQuery(document.body).keydown(function(e) {
if ( e.keyCode == 80 && e.shiftKey && e.altKey && e.ctrlKey ) {
try {
jQuery.sap.require("sap.ui.debug.TechnicalInfo");
} catch (err1) {
// alert("Sorry, failed to activate 'P'-mode!");
return;
}
sap.ui.debug.TechnicalInfo.open(function() {
var oInfo = getModuleSystemInfo();
return { modules : oInfo.modules, prefixes : oInfo.prefixes, config: oCfgData };
});
}
});
});
jQuery(function() {
jQuery(document.body).keydown(function(e) {
if ( e.keyCode == 83 /*S*/ && e.shiftKey && e.altKey && e.ctrlKey ) { //TODO: Is this ok?
try {
jQuery.sap.require("sap.ui.core.support.Support");
var oSupport = sap.ui.core.support.Support.getStub();
if (oSupport.getType() != sap.ui.core.support.Support.StubType.APPLICATION) {
return;
}
oSupport.openSupportTool();
} catch (err2) {
}
}
});
});
}
// *********** Include E2E-Trace Scripts *************
if (/sap-ui-xx-e2e-trace=(true|x|X)/.test(location.search)) {
jQuery.sap.require("sap.ui.core.support.trace.E2eTraceLib" + "" /* Make dynamic dependency */);
}
// *********** feature detection, enriching jQuery.support *************
// this might go into its own file once there is more stuff added
/**
* Holds information about the browser's capabilities and quirks.
* This object is provided and documented by jQuery.
* But it is extended by SAPUI5 with detection for features not covered by jQuery. This documentation ONLY covers the detection properties added by UI5.
* For the standard detection properties, please refer to the jQuery documentation.
*
* These properties added by UI5 are only available temporarily until jQuery adds feature detection on their own.
*
* @name jQuery.support
* @namespace
* @since 1.12
* @public
*/
if (!jQuery.support) {
jQuery.support = {};
}
jQuery.extend(jQuery.support, {touch: sap.ui.Device.support.touch}); // this is also defined by jquery-mobile-custom.js, but this information is needed earlier
var aPrefixes = ["Webkit", "ms", "Moz"];
var oStyle = document.documentElement.style;
var preserveOrTestCssPropWithPrefixes = function(detectionName, propName) {
if (jQuery.support[detectionName] === undefined) {
if (oStyle[propName] !== undefined) { // without vendor prefix
jQuery.support[detectionName] = true;
// If one of the flex layout properties is supported without the prefix, set the flexBoxPrefixed to false
if (propName === "boxFlex" || propName === "flexOrder" || propName === "flexGrow") {
// Exception for Chrome up to version 28
// because some versions implemented the non-prefixed properties without the functionality
if (!sap.ui.Device.browser.chrome || sap.ui.Device.browser.version > 28) {
jQuery.support.flexBoxPrefixed = false;
}
}
return;
} else { // try vendor prefixes
propName = propName.charAt(0).toUpperCase() + propName.slice(1);
for (var i in aPrefixes) {
if (oStyle[aPrefixes[i] + propName] !== undefined) {
jQuery.support[detectionName] = true;
return;
}
}
}
jQuery.support[detectionName] = false;
}
};
/**
* Whether the current browser supports (2D) CSS transforms
* @type {boolean}
* @public
* @name jQuery.support.cssTransforms
*/
preserveOrTestCssPropWithPrefixes("cssTransforms", "transform");
/**
* Whether the current browser supports 3D CSS transforms
* @type {boolean}
* @public
* @name jQuery.support.cssTransforms3d
*/
preserveOrTestCssPropWithPrefixes("cssTransforms3d", "perspective");
/**
* Whether the current browser supports CSS transitions
* @type {boolean}
* @public
* @name jQuery.support.cssTransitions
*/
preserveOrTestCssPropWithPrefixes("cssTransitions", "transition");
/**
* Whether the current browser supports (named) CSS animations
* @type {boolean}
* @public
* @name jQuery.support.cssAnimations
*/
preserveOrTestCssPropWithPrefixes("cssAnimations", "animationName");
/**
* Whether the current browser supports CSS gradients. Note that ANY support for CSS gradients leads to "true" here, no matter what the syntax is.
* @type {boolean}
* @public
* @name jQuery.support.cssGradients
*/
if (jQuery.support.cssGradients === undefined) {
var oElem = document.createElement('div'),
oStyle = oElem.style;
try {
oStyle.backgroundImage = "linear-gradient(left top, red, white)";
oStyle.backgroundImage = "-moz-linear-gradient(left top, red, white)";
oStyle.backgroundImage = "-webkit-linear-gradient(left top, red, white)";
oStyle.backgroundImage = "-ms-linear-gradient(left top, red, white)";
oStyle.backgroundImage = "-webkit-gradient(linear, left top, right bottom, from(red), to(white))";
} catch (e) {/* no support...*/}
jQuery.support.cssGradients = (oStyle.backgroundImage && oStyle.backgroundImage.indexOf("gradient") > -1);
oElem = null; // free for garbage collection
}
/**
* Whether the current browser supports only prefixed flexible layout properties
* @type {boolean}
* @public
* @name jQuery.support.flexBoxPrefixed
*/
jQuery.support.flexBoxPrefixed = true; // Default to prefixed properties
/**
* Whether the current browser supports the OLD CSS3 Flexible Box Layout directly or via vendor prefixes
* @type {boolean}
* @public
* @name jQuery.support.flexBoxLayout
*/
preserveOrTestCssPropWithPrefixes("flexBoxLayout", "boxFlex");
/**
* Whether the current browser supports the IE10 CSS3 Flexible Box Layout directly or via vendor prefixes
* @type {boolean}
* @public
* @name jQuery.support.ie10FlexBoxLayout
* @since 1.12.0
*/
// Just using one of the IE10 properties that's not in the new FlexBox spec
if (oStyle.msFlexOrder !== undefined) {
jQuery.support.ie10FlexBoxLayout = true;
}
/**
* Whether the current browser supports the NEW CSS3 Flexible Box Layout directly or via vendor prefixes
* @type {boolean}
* @public
* @name jQuery.support.newFlexBoxLayout
*/
preserveOrTestCssPropWithPrefixes("newFlexBoxLayout", "flexGrow"); // Use a new property that IE10 doesn't support
/**
* Whether the current browser supports any kind of Flexible Box Layout directly or via vendor prefixes
* @type {boolean}
* @public
* @name jQuery.support.hasFlexBoxSupport
*/
if (jQuery.support.flexBoxLayout || jQuery.support.newFlexBoxLayout || jQuery.support.ie10FlexBoxLayout) {
jQuery.support.hasFlexBoxSupport = true;
} else {
jQuery.support.hasFlexBoxSupport = false;
}
// *********** fixes for (pending) jQuery bugs **********
if (!jQuery.support.opacity) {
(function() {
// jQuery cssHook for setOpacity[IE8] doesn't properly cleanup the CSS filter property
var oldSet = jQuery.cssHooks.opacity.set;
jQuery.cssHooks.opacity.set = function( elem, value ) {
oldSet.apply(this, arguments);
if ( !jQuery.trim(elem.style.filter) ) {
elem.style.removeAttribute("filter");
}
};
}());
}
// *** Performance measure ***
function PerfMeasurement(){
function Measurement( sId, sInfo, iStart, iEnd ){
this.id = sId;
this.info = sInfo;
this.start = iStart;
this.end = iEnd;
this.pause = 0;
this.resume = 0;
this.duration = 0; // used time
this.time = 0; // time from start to end
}
var bActive = false;
var fnAjax = jQuery.ajax;
/**
* Gets the current state of the perfomance measurement functionality
*
* @return {boolean} current state of the perfomance measurement functionality
* @name jQuery.sap.measure#getActive
* @function
* @public
*/
this.getActive = function(){
return bActive;
};
/**
* Activates or deactivates the performance measure functionality
*
* @param {boolean} bOn state of the perfomance measurement functionality to set
* @return {boolean} current state of the perfomance measurement functionality
* @name jQuery.sap.measure#setActive
* @function
* @public
*/
this.setActive = function( bOn ){
if (bActive == bOn) {
return bActive;
}
bActive = bOn;
if (bActive) {
// redefine AJAX call
jQuery.ajax = function( url, options ){
jQuery.sap.measure.start(url.url, "Request for " + url.url);
fnAjax.apply(this,arguments);
jQuery.sap.measure.end(url.url);
};
} else if (fnAjax) {
jQuery.ajax = fnAjax;
}
return bActive;
};
this.setActive(/sap-ui-measure=(true|x|X)/.test(location.search));
this.mMeasurements = {};
/**
* Starts a performance measure
*
* @param {string} sId ID of the measurement
* @param {string} sInfo Info for the measurement
* @return {object} current measurement containing id, info and start-timestamp (false if error)
* @name jQuery.sap.measure#start
* @function
* @public
*/
this.start = function( sId, sInfo ){
if (!bActive) {
return;
}
var iTime = new Date().getTime();
var oMeasurement = new Measurement( sId, sInfo, iTime, 0);
// jQuery.sap.log.info("Performance measurement start: "+ sId + " on "+ iTime);
if (oMeasurement) {
this.mMeasurements[sId] = oMeasurement;
return ({id: oMeasurement.id, info: oMeasurement.info, start: oMeasurement.start });
} else {
return false;
}
};
/**
* Pauses a performance measure
*
* @param {string} sId ID of the measurement
* @return {object} current measurement containing id, info and start-timestamp, pause-timestamp (false if error)
* @name jQuery.sap.measure#pause
* @function
* @public
*/
this.pause = function( sId ){
if (!bActive) {
return;
}
var iTime = new Date().getTime();
var oMeasurement = this.mMeasurements[sId];
if (oMeasurement && oMeasurement.end > 0) {
// already ended -> no pause possible
return false;
}
if (oMeasurement && oMeasurement.pause == 0) {
// not already paused
oMeasurement.pause = iTime;
if (oMeasurement.pause >= oMeasurement.resume && oMeasurement.resume > 0) {
oMeasurement.duration = oMeasurement.duration + oMeasurement.pause - oMeasurement.resume;
oMeasurement.resume = 0;
} else if (oMeasurement.pause >= oMeasurement.start) {
oMeasurement.duration = oMeasurement.pause - oMeasurement.start;
}
}
// jQuery.sap.log.info("Performance measurement pause: "+ sId + " on "+ iTime + " duration: "+ oMeasurement.duration);
if (oMeasurement) {
return ({id: oMeasurement.id, info: oMeasurement.info, start: oMeasurement.start, pause: oMeasurement.pause });
} else {
return false;
}
};
/**
* Resumes a performance measure
*
* @param {string} sId ID of the measurement
* @return {object} current measurement containing id, info and start-timestamp, resume-timestamp (false if error)
* @name jQuery.sap.measure#resume
* @function
* @public
*/
this.resume = function( sId ){
if (!bActive) {
return;
}
var iTime = new Date().getTime();
var oMeasurement = this.mMeasurements[sId];
// jQuery.sap.log.info("Performance measurement resume: "+ sId + " on "+ iTime + " duration: "+ oMeasurement.duration);
if (oMeasurement && oMeasurement.pause > 0) {
// already paused
oMeasurement.pause = 0;
oMeasurement.resume = iTime;
}
if (oMeasurement) {
return ({id: oMeasurement.id, info: oMeasurement.info, start: oMeasurement.start, resume: oMeasurement.resume });
} else {
return false;
}
};
/**
* Ends a performance measure
*
* @param {string} sId ID of the measurement
* @return {object} current measurement containing id, info and start-timestamp, end-timestamp, time, duration (false if error)
* @name jQuery.sap.measure#end
* @function
* @public
*/
this.end = function( sId ){
if (!bActive) {
return;
}
var iTime = new Date().getTime();
var oMeasurement = this.mMeasurements[sId];
// jQuery.sap.log.info("Performance measurement end: "+ sId + " on "+ iTime);
if (oMeasurement && !oMeasurement.end) {
oMeasurement.end = iTime;
if (oMeasurement.end >= oMeasurement.resume && oMeasurement.resume > 0) {
oMeasurement.duration = oMeasurement.duration + oMeasurement.end - oMeasurement.resume;
oMeasurement.resume = 0;
} else if (oMeasurement.pause > 0) {
// duration already calculated
oMeasurement.pause = 0;
} else if (oMeasurement.end >= oMeasurement.start) {
oMeasurement.duration = oMeasurement.end - oMeasurement.start;
}
if (oMeasurement.end >= oMeasurement.start) {
oMeasurement.time = oMeasurement.end - oMeasurement.start;
}
}
if (oMeasurement) {
return ({id: oMeasurement.id,
info: oMeasurement.info,
start: oMeasurement.start,
end: oMeasurement.end,
time: oMeasurement.time,
duration: oMeasurement.duration});
} else {
return false;
}
};
/**
* Gets a performance measure
*
* @param {string} sId ID of the measurement
* @return {object} current measurement containing id, info and start-timestamp, end-timestamp, time, duration (false if error)
* @name jQuery.sap.measure#getMeasurement
* @function
* @public
*/
this.getMeasurement = function( sId ){
if (!bActive) {
return;
}
var oMeasurement = this.mMeasurements[sId];
if (oMeasurement) {
return ({id: oMeasurement.id,
info: oMeasurement.info,
start: oMeasurement.start,
end: oMeasurement.end,
time: oMeasurement.time,
duration: oMeasurement.duration});
} else {
return false;
}
};
/**
* Clears all performance measurements
*
* @name jQuery.sap.measure#clear
* @function
* @public
*/
this.clear = function( ){
if (!bActive) {
return;
}
this.mMeasurements = {};
};
/**
* Removes a performance measure
*
* @param {string} sId ID of the measurement
* @name jQuery.sap.measure#remove
* @function
* @public
*/
this.remove = function( sId ){
if (!bActive) {
return;
}
delete this.mMeasurements[sId];
};
/**
* Gets all performance measurements
*
* @return {object} [] current measurement containing id, info and start-timestamp, end-timestamp, time, duration (false if error)
* @name jQuery.sap.measure#getAllMeasurements
* @function
* @public
*/
this.getAllMeasurements = function( ){
if (!bActive) {
return;
}
var aMeasurements = [];
jQuery.each(this.mMeasurements, function(sId, oMeasurement){
aMeasurements.push({id: oMeasurement.id,
info: oMeasurement.info,
start: oMeasurement.start,
end: oMeasurement.end,
duration: oMeasurement.duration,
time: oMeasurement.time});
});
return aMeasurements;
};
/**
* Adds a performance measurement with all data
* This is usefull to add external measurements (e.g. from a backend) to the common measurement UI
*
* @param {string} sId ID of the measurement
* @param {string} sInfo Info for the measurement
* @param {int} iStart start timestamp
* @param {int} iEnd end timestamp
* @param {int} iTime time in milliseconds
* @param {int} iDuration effective time in milliseconds
* @return {object} [] current measurement containing id, info and start-timestamp, end-timestamp, time, duration (false if error)
* @name jQuery.sap.measure#add
* @function
* @public
*/
this.add = function( sId, sInfo, iStart, iEnd, iTime, iDuration ){
if (!bActive) {
return;
}
var oMeasurement = new Measurement( sId, sInfo, iStart, iEnd);
oMeasurement.time = iTime;
oMeasurement.duration = iDuration;
if (oMeasurement) {
this.mMeasurements[sId] = oMeasurement;
return ({id: oMeasurement.id,
info: oMeasurement.info,
start: oMeasurement.start,
end: oMeasurement.end,
time: oMeasurement.time,
duration: oMeasurement.duration});
} else {
return false;
}
};
}
/**
* Namespace for the jQuery performance measurement plug-in provided by SAP SE.
*
* @namespace
* @name jQuery.sap.measure
* @public
* @static
*/
jQuery.sap.measure = new PerfMeasurement();
/**
* FrameOptions class
*/
var FrameOptions = function(mSettings) {
/* mSettings: mode, callback, whitelist, whitelistService, timeout, blockEvents, showBlockLayer, allowSameOrigin */
this.mSettings = mSettings || {};
this.sMode = this.mSettings.mode || FrameOptions.Mode.ALLOW;
this.fnCallback = this.mSettings.callback;
this.iTimeout = this.mSettings.timeout || 10000;
this.bBlockEvents = this.mSettings.blockEvents !== false;
this.bShowBlockLayer = this.mSettings.showBlockLayer !== false;
this.bAllowSameOrigin = this.mSettings.allowSameOrigin !== false;
this.sParentOrigin = '';
this.bUnlocked = false;
this.bRunnable = false;
this.bParentUnlocked = false;
this.sStatus = "pending";
this.aFPChilds = [];
var that = this;
this.iTimer = setTimeout(function() {
that._callback(false);
}, this.iTimeout);
var fnHandlePostMessage = function() {
that._handlePostMessage.apply(that, arguments);
};
FrameOptions.__window.addEventListener('message', fnHandlePostMessage);
if (FrameOptions.__parent === FrameOptions.__self || FrameOptions.__parent == null || this.sMode === FrameOptions.Mode.ALLOW) {
// unframed page or "allow all" mode
this._applyState(true, true);
} else {
// framed page
this._lock();
// "deny" mode blocks embedding page from all origins
if (this.sMode === FrameOptions.Mode.DENY) {
this._callback(false);
return;
}
if (this.bAllowSameOrigin) {
try {
var oParentWindow = FrameOptions.__parent;
var bOk = false;
var bTrue = true;
do {
var test = oParentWindow.document.domain;
if (oParentWindow == FrameOptions.__top) {
if (test != undefined) {
bOk = true;
}
break;
}
oParentWindow = oParentWindow.parent;
} while (bTrue);
if (bOk) {
this._applyState(true, true);
}
} catch(e) {
// access to the top window is not possible
FrameOptions.__parent.postMessage('SAPFrameProtection*require-origin', '*');
}
} else {
// same origin not allowed
FrameOptions.__parent.postMessage('SAPFrameProtection*require-origin', '*');
}
}
};
FrameOptions.Mode = {
// only allow with same origin parent
TRUSTED: 'trusted',
// allow all kind of embedding (default)
ALLOW: 'allow',
// deny all kinds of embedding
DENY: 'deny'
};
// Allow globals to be mocked in unit test
FrameOptions.__window = window;
FrameOptions.__parent = parent;
FrameOptions.__self = self;
FrameOptions.__top = top;
// List of events to block while framing is unconfirmed
FrameOptions._events = [
"mousedown", "mouseup", "click", "dblclick", "mouseover", "mouseout",
"touchstart", "touchend", "touchmove", "touchcancel",
"keydown", "keypress", "keyup"
];
// check if string matches pattern
FrameOptions.prototype.match = function(sProbe, sPattern) {
if (!(/\*/i.test(sPattern))) {
return sProbe == sPattern;
} else {
sPattern = sPattern.replace(/\//gi, "\\/"); // replace / with \/
sPattern = sPattern.replace(/\./gi, "\\."); // replace . with \.
sPattern = sPattern.replace(/\*/gi, ".*"); // replace * with .*
sPattern = sPattern.replace(/:\.\*$/gi, ":\\d*"); // replace :.* with :\d* (only at the end)
if (sPattern.substr(sPattern.length - 1, 1) !== '$') {
sPattern = sPattern + '$'; // if not already there add $ at the end
}
if (sPattern.substr(0, 1) !== '^') {
sPattern = '^' + sPattern; // if not already there add ^ at the beginning
}
// sPattern looks like: ^.*:\/\/.*\.company\.corp:\d*$ or ^.*\.company\.corp$
var r = new RegExp(sPattern, 'i');
return r.test(sProbe);
}
};
FrameOptions._lockHandler = function(oEvent) {
oEvent.stopPropagation();
oEvent.preventDefault();
};
FrameOptions.prototype._createBlockLayer = function() {
if (document.readyState == "complete") {
var lockDiv = document.createElement("div");
lockDiv.style.position = "absolute";
lockDiv.style.top = "0px";
lockDiv.style.bottom = "0px";
lockDiv.style.left = "0px";
lockDiv.style.right = "0px";
lockDiv.style.opacity = "0";
lockDiv.style.backgroundColor = "white";
lockDiv.style.zIndex = 2147483647; // Max value of signed integer (32bit)
document.body.appendChild(lockDiv);
this._lockDiv = lockDiv;
}
};
FrameOptions.prototype._setCursor = function() {
if (this._lockDiv) {
this._lockDiv.style.cursor = this.sStatus == "denied" ? "not-allowed" : "wait";
}
};
FrameOptions.prototype._lock = function() {
var that = this;
if (this.bBlockEvents) {
for (var i = 0; i < FrameOptions._events.length; i++) {
document.addEventListener(FrameOptions._events[i], FrameOptions._lockHandler, true);
}
}
if (this.bShowBlockLayer) {
this._blockLayer = function() {
that._createBlockLayer();
that._setCursor();
};
if (document.readyState == "complete") {
this._blockLayer();
} else {
document.addEventListener("readystatechange", this._blockLayer);
}
}
};
FrameOptions.prototype._unlock = function() {
if (this.bBlockEvents) {
for (var i = 0; i < FrameOptions._events.length; i++) {
document.removeEventListener(FrameOptions._events[i], FrameOptions._lockHandler, true);
}
}
if (this.bShowBlockLayer) {
document.removeEventListener("readystatechange", this._blockLayer);
if (this._lockDiv) {
document.body.removeChild(this._lockDiv);
delete this._lockDiv;
}
}
};
FrameOptions.prototype._callback = function(bSuccess) {
this.sStatus = bSuccess ? "allowed" : "denied";
this._setCursor();
clearTimeout(this.iTimer);
if (typeof this.fnCallback === 'function') {
this.fnCallback.call(null, bSuccess);
}
};
FrameOptions.prototype._applyState = function(bIsRunnable, bIsParentUnlocked) {
if (bIsRunnable) {
this.bRunnable = true;
}
if (bIsParentUnlocked) {
this.bParentUnlocked = true;
}
if (!this.bRunnable || !this.bParentUnlocked) {
return;
}
this._unlock();
this._callback(true);
this._notifyChildFrames();
this.bUnlocked = true;
};
FrameOptions.prototype._applyTrusted = function(bTrusted) {
if (bTrusted) {
this._applyState(true, false);
} else {
this._callback(false);
}
};
FrameOptions.prototype._check = function() {
if (this.bRunnable) {
return;
}
var bTrusted = false;
if (this.bAllowSameOrigin && FrameOptions.__window.document.URL.indexOf(this.sParentOrigin) == 0) {
bTrusted = true;
} else if (this.mSettings.whitelist && this.mSettings.whitelist.length != 0) {
var sHostName = this.sParentOrigin.split('//')[1];
sHostName = sHostName.split(':')[0];
for (var i = 0; i < this.mSettings.whitelist.length; i++) {
var match = sHostName.indexOf(this.mSettings.whitelist[i]);
if (match != -1 && sHostName.substring(match) == this.mSettings.whitelist[i]) {
bTrusted = true;
break;
}
}
}
if (bTrusted) {
this._applyTrusted(bTrusted);
} else if (this.mSettings.whitelistService) {
var that = this;
var xmlhttp = new XMLHttpRequest();
var url = this.mSettings.whitelistService + '?parentOrigin=' + encodeURIComponent(this.sParentOrigin);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
that._handleXmlHttpResponse(xmlhttp);
}
};
xmlhttp.open('GET', url, true);
xmlhttp.setRequestHeader('Accept', 'application/json');
xmlhttp.send();
} else {
this._callback(false);
}
};
FrameOptions.prototype._handleXmlHttpResponse = function(xmlhttp) {
if (xmlhttp.status === 200) {
var bTrusted = false;
var sResponseText = xmlhttp.responseText;
var oRuleSet = JSON.parse(sResponseText);
if (oRuleSet.active == false) {
bTrusted = true;
} else if (this.match(this.sParentOrigin, oRuleSet.origin)) {
bTrusted = oRuleSet.framing;
}
this._applyTrusted(bTrusted);
} else {
this._callback(false);
}
};
FrameOptions.prototype._notifyChildFrames = function() {
for (var i = 0; i < this.aFPChilds.length; i++) {
this.aFPChilds[i].postMessage('SAPFrameProtection*parent-unlocked','*');
}
};
FrameOptions.prototype._handlePostMessage = function(oEvent) {
var oSource = oEvent.source,
sData = oEvent.data;
// For compatibility with previous version empty message from parent means parent-unlocked
// if (oSource === FrameOptions.__parent && sData == "") {
// sData = "SAPFrameProtection*parent-unlocked";
// }
if (oSource === FrameOptions.__self || oSource == null ||
typeof sData !== "string" || sData.indexOf("SAPFrameProtection*") === -1) {
return;
}
if (oSource === FrameOptions.__parent) {
if (!this.sParentOrigin) {
this.sParentOrigin = oEvent.origin;
this._check();
}
if (sData == "SAPFrameProtection*parent-unlocked") {
this._applyState(false, true);
}
} else if (oSource.parent === FrameOptions.__self && sData == "SAPFrameProtection*require-origin" && this.bUnlocked) {
oSource.postMessage("SAPFrameProtection*parent-unlocked", "*");
} else {
oSource.postMessage("SAPFrameProtection*parent-origin", "*");
this.aFPChilds.push(oSource);
}
};
jQuery.sap.FrameOptions = FrameOptions;
}());
/**
* Executes an 'eval' for its arguments in the global context (without closure variables).
*
* This is a synchronous replacement for <code>jQuery.globalEval</code> which in some
* browsers (e.g. FireFox) behaves asynchronously.
*
* @type void
* @public
* @static
* @SecSink {0|XSS} Parameter is evaluated
*/
jQuery.sap.globalEval = function() {
/*eslint-disable no-eval */
eval(arguments[0]);
/*eslint-enable no-eval */
};
jQuery.sap.declare('sap-ui-core');
jQuery.sap.declare('sap.ui.thirdparty.jquery.jquery-1.11.1', false);
jQuery.sap.declare('sap.ui.Device', false);
jQuery.sap.declare('sap.ui.thirdparty.URI', false);
jQuery.sap.declare('sap.ui.thirdparty.jqueryui.jquery-ui-position', false);
jQuery.sap.declare('jquery.sap.promise', false);
jQuery.sap.declare('jquery.sap.global', false);
jQuery.sap.registerPreloadedModules({
"name":"sap-ui-core-preload",
"version":"2.0",
"modules":{
"jquery.sap.act.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides functionality for activity detection
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
if (typeof window.jQuery.sap.act === "object" || typeof window.jQuery.sap.act === "function" ) {
return;
}
// Date.now = Date.now || function() {
// return new Date().getTime();
// };
/**
* @public
* @name jQuery.sap.act
* @namespace
* @static
*/
var _act = {},
_active = true,
_deactivatetimer = null,
_I_MAX_IDLE_TIME = 10000, //max. idle time in ms
_deactivateSupported = !!window.addEventListener, //Just skip IE8
_aActivateListeners = [],
_activityDetected = false,
_domChangeObserver = null;
function _onDeactivate(){
_deactivatetimer = null;
if (_activityDetected) {
_onActivate();
return;
}
_active = false;
//_triggerEvent(_aDeactivateListeners); //Maybe provide later
_domChangeObserver.observe(document.documentElement, {childList: true, attributes: true, subtree: true, characterData: true});
}
function _onActivate(){
// Never activate when document is not visible to the user
if (document.hidden === true) {
// In case of IE<10 document.visible is undefined, else it is either true or false
return;
}
if (!_active) {
_active = true;
_triggerEvent(_aActivateListeners);
_domChangeObserver.disconnect();
}
if (_deactivatetimer) {
_activityDetected = true;
} else {
_deactivatetimer = setTimeout(_onDeactivate, _I_MAX_IDLE_TIME);
_activityDetected = false;
}
}
function _triggerEvent(aListeners){
if (aListeners.length == 0) {
return;
}
var aEventListeners = aListeners.slice();
setTimeout(function(){
var oInfo;
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
oInfo = aEventListeners[i];
oInfo.fFunction.call(oInfo.oListener || window);
}
}, 0);
}
/**
* Registers the given handler to the activity event, which is fired when an activity was detected after a certain period of inactivity.
*
* The Event is not fired for Internet Explorer 8.
*
* @param {Function} fnFunction The function to call, when an activity event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @protected
*
* @function
* @name jQuery.sap.act#attachActivate
*/
_act.attachActivate = function(fnFunction, oListener){
_aActivateListeners.push({oListener: oListener, fFunction:fnFunction});
};
/**
* Deregisters a previously registered handler from the activity event.
*
* @param {Function} fnFunction The function to call, when an activity event occurs.
* @param {Object} [oListener] The 'this' context of the handler function.
* @protected
*
* @function
* @name jQuery.sap.act#detachActivate
*/
_act.detachActivate = function(fnFunction, oListener){
for (var i = 0, iL = _aActivateListeners.length; i < iL; i++) {
if (_aActivateListeners[i].fFunction === fnFunction && _aActivateListeners[i].oListener === oListener) {
_aActivateListeners.splice(i,1);
break;
}
}
};
/**
* Checks whether recently an activity was detected.
*
* Not supported for Internet Explorer 8.
*
* @return true if recently an activity was detected, false otherwise
* @protected
*
* @function
* @name jQuery.sap.act#isActive
*/
_act.isActive = !_deactivateSupported ? function(){ return true; } : function(){ return _active; };
/**
* Reports an activity.
*
* @public
*
* @function
* @name jQuery.sap.act#refresh
*/
_act.refresh = !_deactivateSupported ? function(){} : _onActivate;
// Setup and registering handlers
if (_deactivateSupported) {
var aEvents = ["resize", "orientationchange", "mousemove", "mousedown", "mouseup", //"mouseout", "mouseover",
"touchstart", "touchmove", "touchend", "touchcancel", "paste", "cut", "keydown", "keyup",
"DOMMouseScroll", "mousewheel"];
for (var i = 0; i < aEvents.length; i++) {
window.addEventListener(aEvents[i], _act.refresh, true);
}
if (window.MutationObserver) {
_domChangeObserver = new window.MutationObserver(_act.refresh);
} else if (window.WebKitMutationObserver) {
_domChangeObserver = new window.WebKitMutationObserver(_act.refresh);
} else {
_domChangeObserver = {
observe : function(){
document.documentElement.addEventListener("DOMSubtreeModified", _act.refresh);
},
disconnect : function(){
document.documentElement.removeEventListener("DOMSubtreeModified", _act.refresh);
}
};
}
if (typeof (document.hidden) === "boolean") {
document.addEventListener("visibilitychange", function() {
// Only trigger refresh if document has changed to visible
if (document.hidden !== true) {
_act.refresh();
}
}, false);
}
_onActivate();
}
jQuery.sap.act = _act;
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.dom.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides functionality related to DOM analysis and manipulation which is not provided by jQuery itself.
sap.ui.define(['jquery.sap.global', 'sap/ui/Device'],
function(jQuery, Device) {
"use strict";
/**
* Shortcut for document.getElementById() with additionally an IE6/7 bug fixed.
* Used to replace the jQuery.sap.domById when running in IE < v8.
*
* @param {string} sId the id of the DOM element to return
* @param {Window} oWindow the window (optional)
* @return {Element} the DOMNode identified by the given sId
* @private
*/
var domByIdInternal = function(sId, oWindow) {
if (!oWindow) {
oWindow = window;
}
if (!sId || sId == "") {
return null;
}
var oDomRef = oWindow.document.getElementById(sId);
// IE also returns the element with the name or id whatever is first
// => the following line makes sure that this was the id
if (oDomRef && oDomRef.id == sId) {
return oDomRef;
}
// otherwise try to lookup the name
var oRefs = oWindow.document.getElementsByName(sId);
for (var i = 0;i < oRefs.length;i++) {
oDomRef = oRefs[i];
if (oDomRef && oDomRef.id == sId) {
return oDomRef;
}
}
return null;
};
/**
* Shortcut for document.getElementById(), including a bug fix for older IE versions.
*
* @param {string} sId The id of the DOM element to return
* @param {Window} [oWindow=window] The window (optional)
* @return {Element} The DOMNode identified by the given sId
* @public
* @function
* @since 0.9.0
*/
jQuery.sap.domById = !!Device.browser.internet_explorer && Device.browser.version < 8 ? domByIdInternal : function domById(sId, oWindow) {
return sId ? (oWindow || window).document.getElementById(sId) : null;
};
/**
* Shortcut for jQuery("#" + id) with additionally the id being escaped properly.
* I.e.: returns the jQuery object for the DOM element with the given id
*
* Use this method instead of jQuery(...) if you know the argument is exactly one id and
* the id is not known in advance because it is in a variable (as opposed to a string
* constant with known content).
*
* @param {string} sId The id to search for and construct the jQuery object
* @param {Element} oContext the context DOM Element
* @return {Object} The jQuery object for the DOM element identified by the given sId
* @public
* @since 0.9.1
*/
jQuery.sap.byId = function byId(sId, oContext) {
var escapedId = "";
if (sId) {
escapedId = "#" + sId.replace(/(:|\.)/g,'\\$1');
}
return jQuery(escapedId, oContext);
};
/**
* Calls focus() on the given DOM element, but catches and ignores any errors that occur when doing so.
* (i.e. IE8 throws an error when the DOM element is invisible or disabled)
*
* @param {Element} oDomRef The DOM element to focus (or null - in this case the method does nothing)
* @return {boolean} Whether the focus() command was executed without an error
* @public
* @since 1.1.2
*/
jQuery.sap.focus = function focus(oDomRef) {
if (!oDomRef) {
return;
}
try {
oDomRef.focus();
} catch (e) {
var id = (oDomRef && oDomRef.id) ? " (ID: '" + oDomRef.id + "')" : "";
jQuery.sap.log.warning("Error when trying to focus a DOM element" + id + ": " + e.message);
return false;
}
return true;
};
/**
* Sets or gets the position of the cursor in an element that supports cursor positioning
*
* @param {int} iPos The cursor position to set (or no parameter to retrieve the cursor position)
* @return {int | jQuery} The cursor position (or the jQuery collection if the position has been set)
* @public
* @name jQuery#cursorPos
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.cursorPos = function cursorPos(iPos) {
var len = arguments.length,
oTextRange,iLength,
sTagName,
sType;
sTagName = this.prop("tagName");
sType = this.prop("type");
if ( this.length === 1 && ((sTagName == "INPUT" && (sType == "text" || sType == "password" || sType == "search"))
|| sTagName == "TEXTAREA" )) {
var oDomRef = this.get(0);
if (len > 0) { // SET
if (typeof (oDomRef.selectionStart) == "number") { // FF and IE9+ method
oDomRef.focus();
oDomRef.selectionStart = iPos;
oDomRef.selectionEnd = iPos;
} else if (oDomRef.createTextRange) { // IE method
oTextRange = oDomRef.createTextRange();
var iMaxLength = oDomRef.value.length;
if (iPos < 0 || iPos > iMaxLength) {
iPos = iMaxLength;
}
if (oTextRange) {
oTextRange.collapse();
oTextRange.moveEnd("character",iPos);
oTextRange.moveStart("character",iPos);
oTextRange.select();
}
}
return this;
// end of SET
} else { // GET
if (typeof (oDomRef.selectionStart) == "number") { // Firefox etc.
return oDomRef.selectionStart;
} else if (oDomRef.createTextRange) { // IE 8
oTextRange = window.document.selection.createRange();
var oCopiedTextRange = oTextRange.duplicate();
// Logic in TEXTAREA and INPUT is different in IE -> check for element type
if (oDomRef.tagName == "TEXTAREA") {
oCopiedTextRange.moveToElementText(oDomRef);
var oCheckTextRange = oCopiedTextRange.duplicate();
iLength = oCopiedTextRange.text.length;
// first check if cursor on last position
oCheckTextRange.moveStart("character", iLength);
var iStart = 0;
if (oCheckTextRange.inRange(oTextRange)) {
iStart = iLength;
} else {
// find out cursor position using a bisection algorithm
var iCheckLength = iLength;
while (iLength > 1) {
iCheckLength = Math.round(iLength / 2);
iStart = iStart + iCheckLength;
oCheckTextRange = oCopiedTextRange.duplicate();
oCheckTextRange.moveStart("character", iStart);
if (oCheckTextRange.inRange(oTextRange)) {
//cursor is after or on iStart -> Length = not checked Length
iLength = iLength - iCheckLength;
} else {
//cursor is before iStart -> Length = checked Length
iStart = iStart - iCheckLength;
iLength = iCheckLength;
}
}
}
return iStart;
} else if (oCopiedTextRange.parentElement() === oDomRef) {
// ensure there is only the cursor and not the range (as this would create erroneous position)!
oCopiedTextRange.collapse();
// now, move the selection range to the beginning of the inputField and simply get the selected range's length
var iLength = oDomRef.value.length;
oCopiedTextRange.moveStart('character', -iLength);
return oCopiedTextRange.text.length;
}
}
return -1;
} // end of GET
} else {
// shouldn't really happen, but to be safe...
return this;
}
};
/**
* Sets the text selection in the first element of the collection.
* note: This feature is only supported for input elements type of text, search, url, tel and password.
*
* @param {int} iStart Start position of the selection (inclusive)
* @param {int} iEnd End position of the selection (exclusive)
* @return {jQuery} The jQuery collection
* @public
* @name jQuery#selectText
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.selectText = function selectText(iStart, iEnd) {
var oDomRef = this.get(0);
try {
if (typeof (oDomRef.selectionStart) === "number") { // Firefox and IE9+
oDomRef.setSelectionRange(iStart, iEnd);
} else if (oDomRef.createTextRange) { // IE
var oTextEditRange = oDomRef.createTextRange();
oTextEditRange.collapse();
oTextEditRange.moveStart('character', iStart);
oTextEditRange.moveEnd('character', iEnd - iStart);
oTextEditRange.select();
}
} catch (e) {} // note: some browsers fail to read the "selectionStart" and "selectionEnd" properties from HTMLInputElement, e.g.: The input element's type "number" does not support selection.
return this;
};
/**
* Retrieve the selected text in the first element of the collection.
* note: This feature is only supported for input elements type of text, search, url, tel and password.
*
* @return {string} The selected text.
* @public
* @name jQuery#getSelectedText
* @author SAP SE
* @since 1.26.0
* @function
*/
jQuery.fn.getSelectedText = function() {
var oDomRef = this.get(0);
try {
if (typeof oDomRef.selectionStart === "number") {
return oDomRef.value.substring(oDomRef.selectionStart, oDomRef.selectionEnd);
}
// older versions of Internet Explorer do not support the HTML5 "selectionStart" and "selectionEnd" properties
if (document.selection) {
return document.selection.createRange().text;
}
} catch (e) {} // note: some browsers fail to read the "selectionStart" and "selectionEnd" properties from HTMLInputElement, e.g.: The input element's type "number" does not support selection.
return "";
};
/**
* Returns the outer HTML of the given HTML element
*
* @return {string} outer HTML
* @public
* @name jQuery#outerHTML
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.outerHTML = function outerHTML() {
var oDomRef = this.get(0);
if (oDomRef && oDomRef.outerHTML) {
return jQuery.trim(oDomRef.outerHTML);
} else {
var doc = this[0] ? this[0].ownerDocument : document;
var oDummy = doc.createElement("div");
oDummy.appendChild(oDomRef.cloneNode(true));
return oDummy.innerHTML;
}
};
/**
* Returns whether oDomRefChild is oDomRefContainer or is contained in oDomRefContainer.
*
* This is a browser-independent version of the .contains method of Internet Explorer.
* For compatibility reasons it returns true if oDomRefContainer and oDomRefChild are equal.
*
* This method intentionally does not operate on the jQuery object, as the original jQuery.contains()
* method also does not do so.
*
* @param {Element} oDomRefContainer The container element
* @param {Element} oDomRefChild The child element (must not be a text node, must be an element)
* @return {boolean} 'true' if oDomRefChild is contained in oDomRefContainer or oDomRefChild is oDomRefContainer
* @public
* @author SAP SE
* @since 0.9.0
*/
jQuery.sap.containsOrEquals = function containsOrEquals(oDomRefContainer, oDomRefChild) {
if (oDomRefChild && oDomRefContainer && oDomRefChild != document && oDomRefChild != window) {
return (oDomRefContainer === oDomRefChild) || jQuery.contains(oDomRefContainer, oDomRefChild);
}
return false;
};
/**
* Returns a rectangle describing the current visual positioning of the first DOM object in the collection
* (or null if no element was given)
*
* @return {object} An object with left, top, width and height
* @public
* @name jQuery#rect
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.rect = function rect() {
var oDomRef = this.get(0);
if (oDomRef) {
// this should be available in all 'modern browsers'
if (oDomRef.getBoundingClientRect) {
var oClientRect = oDomRef.getBoundingClientRect();
var oRect = { top : oClientRect.top,
left : oClientRect.left,
width : oClientRect.right - oClientRect.left,
height : oClientRect.bottom - oClientRect.top };
var oWnd = jQuery.sap.ownerWindow(oDomRef);
oRect.left += jQuery(oWnd).scrollLeft();
oRect.top += jQuery(oWnd).scrollTop();
return oRect;
} else {
// IE6 and older; avoid crashing and give some hardcoded size
return { top : 10, left : 10, width : oDomRef.offsetWidth, height : oDomRef.offsetWidth };
}
}
return null;
};
/**
* Returns whether a point described by X and Y is inside this Rectangle's boundaries
*
* @param {int} iPosX
* @param {int} iPosY
* @return {boolean} Whether X and Y are inside this Rectangle's boundaries
* @public
* @name jQuery#rectContains
* @author SAP SE
* @since 0.18.0
* @function
*/
jQuery.fn.rectContains = function rectContains(iPosX, iPosY) {
jQuery.sap.assert(!isNaN(iPosX), "iPosX must be a number");
jQuery.sap.assert(!isNaN(iPosY), "iPosY must be a number");
var oRect = this.rect();
if (oRect) {
return iPosX >= oRect.left
&& iPosX <= oRect.left + oRect.width
&& iPosY >= oRect.top
&& iPosY <= oRect.top + oRect.height;
}
return false;
};
/**
* Returns true if the first element has a set tabindex
*
* @return {boolean} If the first element has a set tabindex
* @public
* @name jQuery#hasTabIndex
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.hasTabIndex = function hasTabIndex() {
var iTabIndex = this.prop("tabIndex");
if (this.attr("disabled") && !this.attr("tabindex")) {
// disabled field with not explicit set tabindex -> not in tab chain (bug of jQuery prop function)
iTabIndex = -1;
}
return !isNaN(iTabIndex) && iTabIndex >= 0;
};
/**
* Returns the first focusable domRef in a given container (the first element of the collection)
*
* @return {Element} The domRef
* @public
* @name jQuery#firstFocusableDomRef
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.firstFocusableDomRef = function firstFocusableDomRef() {
var oContainerDomRef = this.get(0);
var visibilityHiddenFilter = function (idx){
return jQuery(this).css("visibility") == "hidden";
};
if (!oContainerDomRef || jQuery(oContainerDomRef).is(':hidden') ||
jQuery(oContainerDomRef).filter(visibilityHiddenFilter).length == 1) {
return null;
}
var oCurrDomRef = oContainerDomRef.firstChild,
oDomRefFound = null;
while (oCurrDomRef) {
if (oCurrDomRef.nodeType == 1 && jQuery(oCurrDomRef).is(':visible')) {
if (jQuery(oCurrDomRef).hasTabIndex()) {
return oCurrDomRef;
}
if (oCurrDomRef.childNodes) {
oDomRefFound = jQuery(oCurrDomRef).firstFocusableDomRef();
if (oDomRefFound) {
return oDomRefFound;
}
}
}
oCurrDomRef = oCurrDomRef.nextSibling;
}
return null;
};
/**
* Returns the last focusable domRef in a given container
*
* @return {Element} The last domRef
* @public
* @name jQuery#lastFocusableDomRef
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.lastFocusableDomRef = function lastFocusableDomRef() {
var oContainerDomRef = this.get(0);
var visibilityHiddenFilter = function (idx){
return jQuery(this).css("visibility") == "hidden";
};
if (!oContainerDomRef || jQuery(oContainerDomRef).is(':hidden') ||
jQuery(oContainerDomRef).filter(visibilityHiddenFilter).length == 1) {
return null;
}
var oCurrDomRef = oContainerDomRef.lastChild,
oDomRefFound = null;
while (oCurrDomRef) {
if (oCurrDomRef.nodeType == 1 && jQuery(oCurrDomRef).is(':visible')) {
if (oCurrDomRef.childNodes) {
oDomRefFound = jQuery(oCurrDomRef).lastFocusableDomRef();
if (oDomRefFound) {
return oDomRefFound;
}
}
if (jQuery(oCurrDomRef).hasTabIndex()) {
return oCurrDomRef;
}
}
oCurrDomRef = oCurrDomRef.previousSibling;
}
return null;
};
/**
* Sets or returns the scrollLeft value of the first element in the given jQuery collection in right-to-left mode.
* Precondition: The element is rendered in RTL mode.
*
* Reason for this method is that the major browsers use three different values for the same scroll position when in RTL mode.
* This method hides those differences and returns/applies the same value that would be returned in LTR mode: The distance in px
* how far the given container is scrolled away from the leftmost scroll position.
*
* Returns "undefined" if no element and no iPos is given.
*
* @param {int} iPos
* @return {jQuery | int} The jQuery collection if iPos is given, otherwise the scroll position, counted from the leftmost position
* @public
* @name jQuery#scrollLeftRTL
* @author SAP SE
* @since 0.20.0
* @function
*/
jQuery.fn.scrollLeftRTL = function scrollLeftRTL(iPos) {
var oDomRef = this.get(0);
if (oDomRef) {
if (iPos === undefined) { // GETTER code
if (!!Device.browser.internet_explorer) {
return oDomRef.scrollWidth - oDomRef.scrollLeft - oDomRef.clientWidth;
} else if (!!Device.browser.webkit) {
return oDomRef.scrollLeft;
} else if (!!Device.browser.firefox) {
return oDomRef.scrollWidth + oDomRef.scrollLeft - oDomRef.clientWidth;
} else {
// unrecognized browser; it is hard to return a best guess, as browser strategies are very different, so return the actual value
return oDomRef.scrollLeft;
}
} else { // SETTER code
oDomRef.scrollLeft = jQuery.sap.denormalizeScrollLeftRTL(iPos, oDomRef);
return this;
}
}
};
/**
* Returns the MIRRORED scrollLeft value of the first element in the given jQuery collection in right-to-left mode.
* Precondition: The element is rendered in RTL mode.
*
* Reason for this method is that the major browsers return three different values for the same scroll position when in RTL mode.
* This method hides those differences and returns the value that would be returned in LTR mode if the UI would be mirrored horizontally:
* The distance in px how far the given container is scrolled away from the rightmost scroll position.
*
* Returns "undefined" if no element is given.
*
* @return {int} The scroll position, counted from the rightmost position
* @public
* @name jQuery#scrollRightRTL
* @author SAP SE
* @since 0.20.0
* @function
*/
jQuery.fn.scrollRightRTL = function scrollRightRTL() {
var oDomRef = this.get(0);
if (oDomRef) {
if (!!Device.browser.internet_explorer) {
return oDomRef.scrollLeft;
} else if (!!Device.browser.webkit) {
return oDomRef.scrollWidth - oDomRef.scrollLeft - oDomRef.clientWidth;
} else if (!!Device.browser.firefox) {
return (-oDomRef.scrollLeft);
} else {
// unrecognized browser; it is hard to return a best guess, as browser strategies are very different, so return the actual value
return oDomRef.scrollLeft;
}
}
};
/**
* For the given scrollLeft value this method returns the scrollLeft value as understood by the current browser in RTL mode.
* This value is specific to the given DOM element, as the computation may involve its dimensions.
*
* So when oDomRef should be scrolled 2px from the leftmost position, the number "2" must be given as iNormalizedScrollLeft
* and the result of this method (which may be a large or even negative number, depending on the browser) can then be set as
* oDomRef.scrollLeft to achieve the desired (cross-browser-consistent) scrolling position.
*
* This method does no scrolling on its own, it only calculates the value to set (so it can also be used for animations).
*
* @param {int} iNormalizedScrollLeft The distance from the leftmost position to which the element should be scrolled
* @param {Element} oDomRef The DOM Element to which scrollLeft will be applied
* @return {int} The scroll position that must be set for the DOM element
* @public
* @author SAP SE
* @since 0.20.0
*/
jQuery.sap.denormalizeScrollLeftRTL = function(iNormalizedScrollLeft, oDomRef) {
if (oDomRef) {
if (!!Device.browser.internet_explorer) {
return oDomRef.scrollWidth - oDomRef.clientWidth - iNormalizedScrollLeft;
} else if (!!Device.browser.webkit) {
return iNormalizedScrollLeft;
} else if (!!Device.browser.firefox) {
return oDomRef.clientWidth + iNormalizedScrollLeft - oDomRef.scrollWidth;
} else {
// unrecognized browser; it is hard to return a best guess, as browser strategies are very different, so return the actual value
return iNormalizedScrollLeft;
}
}
};
/**
* For the given scroll position measured from the "beginning" of a container (the right edge in RTL mode)
* this method returns the scrollLeft value as understood by the current browser in RTL mode.
* This value is specific to the given DOM element, as the computation may involve its dimensions.
*
* So when oDomRef should be scrolled 2px from the beginning, the number "2" must be given as iNormalizedScrollBegin
* and the result of this method (which may be a large or even negative number, depending on the browser) can then be set as
* oDomRef.scrollLeft to achieve the desired (cross-browser-consistent) scrolling position.
* Low values make the right part of the content visible, high values the left part.
*
* This method does no scrolling on its own, it only calculates the value to set (so it can also be used for animations).
*
* Only use this method in RTL mode, as the behavior in LTR mode is undefined and may change!
*
* @param {int} iNormalizedScrollBegin The distance from the rightmost position to which the element should be scrolled
* @param {Element} oDomRef The DOM Element to which scrollLeft will be applied
* @return {int} The scroll position that must be set for the DOM element
* @public
* @author SAP SE
* @since 1.26.1
*/
jQuery.sap.denormalizeScrollBeginRTL = function(iNormalizedScrollBegin, oDomRef) {
if (oDomRef) {
if (!!Device.browser.internet_explorer) {
return iNormalizedScrollBegin;
} else if (!!Device.browser.webkit) {
return oDomRef.scrollWidth - oDomRef.clientWidth - iNormalizedScrollBegin;
} else if (!!Device.browser.firefox) {
return -iNormalizedScrollBegin;
} else {
// unrecognized browser; it is hard to return a best guess, as browser strategies are very different, so return the actual value
return iNormalizedScrollBegin;
}
}
};
/*
* The following methods are taken from jQuery UI core but modified.
*
* jQuery UI Core
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/
jQuery.support.selectstart = "onselectstart" in document.createElement("div");
jQuery.fn.extend( /** @lends jQuery.prototype */ {
/**
* Disable HTML elements selection.
*
* @return {jQuery} <code>this</code> to allow method chaining.
* @protected
* @since 1.24.0
*/
disableSelection: function() {
return this.on((jQuery.support.selectstart ? "selectstart" : "mousedown") + ".ui-disableSelection", function(oEvent) {
oEvent.preventDefault();
});
},
/**
* Enable HTML elements to get selected.
*
* @return {jQuery} <code>this</code> to allow method chaining.
* @protected
* @since 1.24.0
*/
enableSelection: function() {
return this.off(".ui-disableSelection");
}
});
/*!
* The following functions are taken from jQuery UI 1.8.17 but modified
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI
*/
function visible( element ) {
// check if one of the parents (until it's position parent) is invisible
// prevent that elements in static area are always checked as invisible
// list all items until the offsetParent item (with jQuery >1.6 you can use parentsUntil)
var oOffsetParent = jQuery(element).offsetParent();
var bOffsetParentFound = false;
var $refs = jQuery(element).parents().filter(function() {
if (this === oOffsetParent) {
bOffsetParentFound = true;
}
return bOffsetParentFound;
});
// check for at least one item to be visible
return !jQuery(element).add($refs).filter(function() {
return jQuery.css( this, "visibility" ) === "hidden" || jQuery.expr.filters.hidden( this );
}).length;
}
function focusable( element, isTabIndexNotNaN ) {
var nodeName = element.nodeName.toLowerCase();
if ( nodeName === "area" ) {
var map = element.parentNode,
mapName = map.name,
img;
if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
return false;
}
img = jQuery( "img[usemap=#" + mapName + "]" )[0];
return !!img && visible( img );
}
/*eslint-disable no-nested-ternary */
return ( /input|select|textarea|button|object/.test( nodeName )
? !element.disabled
: nodeName == "a"
? element.href || isTabIndexNotNaN
: isTabIndexNotNaN)
// the element and all of its ancestors must be visible
&& visible( element );
/*eslint-enable no-nested-ternary */
}
if (!jQuery.expr[":"].focusable) {
/*!
* The following function is taken from jQuery UI 1.8.17
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI
*
* But since visible is modified, focusable is different too the jQuery UI version too.
*/
jQuery.extend( jQuery.expr[ ":" ], {
/**
* This defines the jQuery ":focusable" selector; it is also defined in jQuery UI. If already present, nothing is
* done here, so we will not overwrite any previous implementation.
* If jQuery UI is loaded later on, this implementation here will be overwritten by that one, which is fine,
* as it is semantically the same thing and intended to do exactly the same.
*/
focusable: function( element ) {
return focusable( element, !isNaN( jQuery.attr( element, "tabindex" ) ) );
}
});
}
if (!jQuery.expr[":"].sapTabbable) {
/*!
* The following function is taken from
* jQuery UI Core 1.11.1
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/
jQuery.extend( jQuery.expr[ ":" ], {
/**
* This defines the jQuery ":tabbable" selector; it is also defined in jQuery UI. If already present, nothing is
* done here, so we will not overwrite any previous implementation.
* If jQuery UI is loaded later on, this implementation here will be overwritten by that one, which is fine,
* as it is semantically the same thing and intended to do exactly the same.
*/
sapTabbable: function( element ) {
var tabIndex = jQuery.attr( element, "tabindex" ),
isTabIndexNaN = isNaN( tabIndex );
return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
}
});
}
if (!jQuery.expr[":"].sapFocusable) {
/*!
* Do not use jQuery UI focusable because this might be overwritten if jQuery UI is loaded
*/
jQuery.extend( jQuery.expr[ ":" ], {
/**
* This defines the jQuery ":sapFocusable" selector; If already present, nothing is
* done here, so we will not overwrite any previous implementation.
* If jQuery UI is loaded later on, this implementation here will NOT be overwritten by.
*/
sapFocusable: function( element ) {
return focusable( element, !isNaN( jQuery.attr( element, "tabindex" ) ) );
}
});
}
if (!jQuery.fn.zIndex) {
/*!
* The following function is taken from
* jQuery UI Core 1.11.1
* http://jqueryui.com
*
* Copyright 2014 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/
jQuery.fn.zIndex = function( zIndex ) {
if ( zIndex !== undefined ) {
return this.css( "zIndex", zIndex );
}
if ( this.length ) {
var elem = jQuery( this[ 0 ] ), position, value;
while ( elem.length && elem[ 0 ] !== document ) {
// Ignore z-index if position is set to a value where z-index is ignored by the browser
// This makes behavior of this function consistent across browsers
// WebKit always returns auto if the element is positioned
position = elem.css( "position" );
if ( position === "absolute" || position === "relative" || position === "fixed" ) {
// IE returns 0 when zIndex is not specified
// other browsers return a string
// we ignore the case of nested elements with an explicit value of 0
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
value = parseInt( elem.css( "zIndex" ), 10 );
if ( !isNaN( value ) && value !== 0 ) {
return value;
}
}
elem = elem.parent();
}
}
return 0;
};
}
/**
* Gets the next parent DOM element with a given attribute and attribute value starting above the first given element
*
* @param {string} sAttribute Name of the attribute
* @param {string} sValue Value of the attribute (optional)
* @return {Element} null or the DOM reference
* @public
* @name jQuery#parentByAttribute
* @author SAP SE
* @since 0.9.0
* @function
*/
jQuery.fn.parentByAttribute = function parentByAttribute(sAttribute, sValue) {
if (this.length > 0) {
if (sValue) {
return this.first().parents("[" + sAttribute + "='" + sValue + "']").get(0);
} else {
return this.first().parents("[" + sAttribute + "]").get(0);
}
}
};
/**
* Returns the window reference for a DomRef
*
* @param {Element} oDomRef The DOM reference
* @return {Window} Window reference
* @public
* @since 0.9.0
*/
jQuery.sap.ownerWindow = function ownerWindow(oDomRef){
if (oDomRef.ownerDocument.parentWindow) {
return oDomRef.ownerDocument.parentWindow;
}
return oDomRef.ownerDocument.defaultView;
};
var _oScrollbarSize = {};
/**
* Returns the size (width of the vertical / height of the horizontal) native browser scrollbars.
*
* This function must only be used when the DOM is ready.
*
* @param {string} [sClasses=null] the CSS class that should be added to the test element.
* @param {boolean} [bForce=false] force recalculation of size (e.g. when CSS was changed). When no classes are passed all calculated sizes are reset.
* @return {object} JSON object with properties <code>width</code> and <code>height</code> (the values are of type number and are pixels).
* @public
* @since 1.4.0
*/
jQuery.sap.scrollbarSize = function(sClasses, bForce) {
if (typeof sClasses === "boolean") {
bForce = sClasses;
sClasses = null;
}
var sKey = sClasses || "#DEFAULT"; // # is an invalid character for CSS classes
if (bForce) {
if (sClasses) {
delete _oScrollbarSize[sClasses];
} else {
_oScrollbarSize = {};
}
}
if (_oScrollbarSize[sKey]) {
return _oScrollbarSize[sKey];
}
if (!document.body) {
return {width: 0, height: 0};
}
var $Area = jQuery("<DIV/>")
.css("visibility", "hidden")
.css("height", "0")
.css("width", "0")
.css("overflow", "hidden");
if (sClasses) {
$Area.addClass(sClasses);
}
$Area.prependTo(document.body);
var $Dummy = jQuery("<div style=\"visibility:visible;position:absolute;height:100px;width:100px;overflow:scroll;opacity:0;\"></div>");
$Area.append($Dummy);
var oDomRef = $Dummy.get(0);
var iWidth = oDomRef.offsetWidth - oDomRef.scrollWidth;
var iHeight = oDomRef.offsetHeight - oDomRef.scrollHeight;
$Area.remove();
// due to a bug in FireFox when hiding iframes via an outer DIV element
// the height and width calculation is not working properly - by not storing
// height and width when one value is 0 we make sure that once the iframe
// gets visible the height calculation will be redone (see snippix: #64049)
if (iWidth === 0 || iHeight === 0) {
return {width: iWidth, height: iHeight};
}
_oScrollbarSize[sKey] = {width: iWidth, height: iHeight};
return _oScrollbarSize[sKey];
};
/**
* Search ancestors of the given source DOM element for the specified CSS class name.
* If the class name is found, set it to the root DOM element of the target control.
* If the class name is not found, it is also removed from the target DOM element.
*
* @param {string} sStyleClass CSS class name
* @param {jQuery|Control|string} vSource jQuery object, control or an id of the source element.
* @param {jQuery|Control} vDestination target jQuery object or a control.
* @return {jQuery|Element} Target element
* @public
* @since 1.22
*/
jQuery.sap.syncStyleClass = function(sStyleClass, vSource, vDestination) {
if (!sStyleClass) {
return vDestination;
}
if (vSource instanceof sap.ui.core.Control) {
vSource = vSource.$();
} else if (typeof vSource === "string") {
vSource = jQuery.sap.byId(vSource);
} else if (!(vSource instanceof jQuery)) {
jQuery.sap.assert(false, 'jQuery.sap.syncStyleClass(): vSource must be a jQuery object or a Control or a string');
return vDestination;
}
var bClassFound = !!vSource.closest("." + sStyleClass).length;
if (vDestination instanceof jQuery) {
vDestination.toggleClass(sStyleClass, bClassFound);
} else if (vDestination instanceof sap.ui.core.Control) {
vDestination.toggleStyleClass(sStyleClass, bClassFound);
} else {
jQuery.sap.assert(false, 'jQuery.sap.syncStyleClass(): vDestination must be a jQuery object or a Control');
}
return vDestination;
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.encoder.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides encoding functions for JavaScript.
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/*
* Encoding according to the Secure Programming Guide
* <SAPWIKI>/wiki/display/NWCUIAMSIM/XSS+Secure+Programming+Guide
*/
/**
* Create hex and pad to length
* @private
*/
function hex(iChar, iLength) {
var sHex = iChar.toString(16);
if (iLength) {
while (iLength > sHex.length) {
sHex = "0" + sHex;
}
}
return sHex;
}
/**
* RegExp and escape function for HTML escaping
*/
var rHtml = /[\x00-\x2b\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff\u2028\u2029]/g,
rHtmlReplace = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/,
mHtmlLookup = {
"<": "&lt;",
">": "&gt;",
"&": "&amp;",
"\"": "&quot;"
};
var fHtml = function(sChar) {
var sEncoded = mHtmlLookup[sChar];
if (!sEncoded) {
if (rHtmlReplace.test(sChar)) {
sEncoded = "&#xfffd;";
} else {
sEncoded = "&#x" + hex(sChar.charCodeAt(0)) + ";";
}
mHtmlLookup[sChar] = sEncoded;
}
return sEncoded;
};
/**
* Encode the string for inclusion into HTML content/attribute
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for HTML contexts
*/
jQuery.sap.encodeHTML = function(sString) {
return sString.replace(rHtml, fHtml);
};
/**
* Encode the string for inclusion into XML content/attribute
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for XML contexts
*/
jQuery.sap.encodeXML = function(sString) {
return sString.replace(rHtml, fHtml);
};
/**
* Encode the string for inclusion into HTML content/attribute.
* Old name "escapeHTML" kept for backward compatibility
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @deprecated Has been renamed, use {@link jQuery.sap.encodeHTML} instead.
*/
jQuery.sap.escapeHTML = function(sString) {
return sString.replace(rHtml, fHtml);
};
/**
* RegExp and escape function for JS escaping
*/
var rJS = /[\x00-\x2b\x2d\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff\u2028\u2029]/g,
mJSLookup = {};
var fJS = function(sChar) {
var sEncoded = mJSLookup[sChar];
if (!sEncoded) {
var iChar = sChar.charCodeAt(0);
if (iChar < 256) {
sEncoded = "\\x" + hex(iChar, 2);
} else {
sEncoded = "\\u" + hex(iChar, 4);
}
mJSLookup[sChar] = sEncoded;
}
return sEncoded;
};
/**
* Encode the string for inclusion into a JS string literal
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for a JavaScript contexts
*/
jQuery.sap.encodeJS = function(sString) {
return sString.replace(rJS, fJS);
};
/**
* Encode the string for inclusion into a JS string literal.
* Old name "escapeJS" kept for backward compatibility
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @deprecated Since 1.3.0. Has been renamed, use {@link jQuery.sap.encodeJS} instead.
*/
jQuery.sap.escapeJS = function(sString) {
return sString.replace(rJS, fJS);
};
/**
* RegExp and escape function for URL escaping
*/
var rURL = /[\x00-\x2c\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\uffff]/g,
mURLLookup = {};
var fURL = function(sChar) {
var sEncoded = mURLLookup[sChar];
if (!sEncoded) {
var iChar = sChar.charCodeAt(0);
if (iChar < 128) {
sEncoded = "%" + hex(iChar, 2);
} else if (iChar < 2048) {
sEncoded = "%" + hex((iChar >> 6) | 192, 2) +
"%" + hex((iChar & 63) | 128, 2);
} else {
sEncoded = "%" + hex((iChar >> 12) | 224, 2) +
"%" + hex(((iChar >> 6) & 63) | 128, 2) +
"%" + hex((iChar & 63) | 128, 2);
}
mURLLookup[sChar] = sEncoded;
}
return sEncoded;
};
/**
* Encode the string for inclusion into an URL parameter
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for a URL context
*/
jQuery.sap.encodeURL = function(sString) {
return sString.replace(rURL, fURL);
};
/**
* Encode a map of parameters into a combined URL parameter string
*
* @param {object} mParams The map of parameters to encode
* @return The URL encoded parameters
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for a CSS context
*/
jQuery.sap.encodeURLParameters = function(mParams) {
if (!mParams) {
return "";
}
var aUrlParams = [];
jQuery.each(mParams, function (sName, oValue) {
if (jQuery.type(oValue) === "string") {
oValue = jQuery.sap.encodeURL(oValue);
}
aUrlParams.push(jQuery.sap.encodeURL(sName) + "=" + oValue);
});
return aUrlParams.join("&");
};
/**
* RegExp and escape function for CSS escaping
*/
var rCSS = /[\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff\u2028\u2029][0-9A-Fa-f]?/g;
var fCSS = function(sChar) {
var iChar = sChar.charCodeAt(0);
if (sChar.length == 1) {
return "\\" + hex(iChar);
} else {
return "\\" + hex(iChar) + " " + sChar.substr(1);
}
};
/**
* Encode the string for inclusion into CSS string literals or identifiers
*
* @param {string} sString The string to be escaped
* @return The escaped string
* @type {string}
* @public
* @SecValidate {0|return|XSS} validates the given string for a CSS context
*/
jQuery.sap.encodeCSS = function(sString) {
return sString.replace(rCSS, fCSS);
};
/**
* WhitelistEntry object
* @param {string} protocol The protocol of the URL
* @param {string} host The host of the URL
* @param {string} port The port of the URL
* @param {string} path the path of the URL
* @public
*/
function WhitelistEntry(protocol, host, port, path){
if (protocol) {
this.protocol = protocol.toUpperCase();
}
if (host) {
this.host = host.toUpperCase();
}
this.port = port;
this.path = path;
}
var aWhitelist = [];
/**
* clears the whitelist for URL valiadtion
*
* @public
*/
jQuery.sap.clearUrlWhitelist = function() {
aWhitelist.splice(0,aWhitelist.length);
};
/**
* Adds a whitelist entry for URL valiadtion
*
* @param {string} protocol The protocol of the URL
* @param {string} host The host of the URL
* @param {string} port The port of the URL
* @param {string} path the path of the URL
* @public
*/
jQuery.sap.addUrlWhitelist = function(protocol, host, port, path) {
var oEntry = new WhitelistEntry(protocol, host, port, path);
var iIndex = aWhitelist.length;
aWhitelist[iIndex] = oEntry;
};
/**
* Removes a whitelist entry for URL valiadtion
*
* @param {int} iIndex index of entry
* @public
*/
jQuery.sap.removeUrlWhitelist = function(iIndex) {
aWhitelist.splice(iIndex,1);
};
/**
* Gets the whitelist for URL valiadtion
*
* @return {string[]} whitelist
* @public
*/
jQuery.sap.getUrlWhitelist = function() {
return aWhitelist.slice();
};
/**
* Validates an URL. Check if it's not a script or other security issue.
*
* @param {string} sUrl
* @return true if valid, false if not valid
* @public
*/
jQuery.sap.validateUrl = function(sUrl) {
var result = /(?:([^:\/?#]+):)?(?:\/\/([^\/?#:]*)(?::([0-9]+))?)?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/.exec(sUrl);
if (!result) {
return result;
}
var sProtocol = result[1],
sHost = result[2],
sPort = result[3],
sPath = result[4],
sQuery = result[5],
sHash = result[6];
var rCheck = /[\x00-\x24\x26-\x29\x2b\x2c\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\x7d\x7f-\uffff]/;
var rCheckHash = /[\x00-\x24\x26-\x29\x2b\x2c\x3a-\x3e\x5b-\x5e\x60\x7b-\x7d\x7f-\uffff]/;
var rCheckMail = /[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
// protocol
if (sProtocol) {
sProtocol = sProtocol.toUpperCase();
if (aWhitelist.length <= 0) {
// no whitelist -> check for default protocols
if (!/^(https?|ftp)/i.test(sProtocol)) {
return false;
}
}
}
// Host -> whitelist + character check (TBD)
if (sHost) {
sHost = sHost.toUpperCase();
}
// Path -> split for "/" and check if forbidden characters exist
if (sPath) {
if (sProtocol === "MAILTO") {
var bCheck = rCheckMail.test(sPath);
if (!bCheck) {
return false;
}
} else {
var aComponents = sPath.split("/");
for ( var i = 0; i < aComponents.length; i++) {
var bCheck = rCheck.test(aComponents[i]);
if (bCheck) {
// forbidden character found
return false;
}
}
}
}
// query -> Split on & and = and check if forbidden characters exist
if (sQuery) {
var aComponents = sQuery.split("&");
for ( var i = 0; i < aComponents.length; i++) {
var iPos = aComponents[i].search("=");
if (iPos != -1) {
var sPart1 = aComponents[i].substring(0,iPos);
var sPart2 = aComponents[i].substring(iPos + 1);
var bCheck1 = rCheck.test(sPart1);
var bCheck2 = rCheck.test(sPart2);
if (bCheck1 || bCheck2) {
// forbidden character found
return false;
}
}
}
}
// hash
if (sHash) {
if (rCheckHash.test(sHash)) {
// forbidden character found
return false;
}
}
//filter whitelist
if (aWhitelist.length > 0) {
var bFound = false;
for (var i = 0; i < aWhitelist.length; i++) {
jQuery.sap.assert(aWhitelist[i] instanceof WhitelistEntry, "whitelist entry type wrong");
if (!sProtocol || !aWhitelist[i].protocol || sProtocol == aWhitelist[i].protocol) {
// protocol OK
var bOk = false;
if (sHost && aWhitelist[i].host && /^\*/.test(aWhitelist[i].host)) {
// check for wildcard search at begin
var sHostEscaped = aWhitelist[i].host.slice(1).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
var rFilter = RegExp(sHostEscaped + "$");
if (rFilter.test(sHost)) {
bOk = true;
}
} else if (!sHost || !aWhitelist[i].host || sHost == aWhitelist[i].host) {
bOk = true;
}
if (bOk) {
// host OK
if ((!sHost && !sPort) || !aWhitelist[i].port || sPort == aWhitelist[i].port) {
// port OK
if (aWhitelist[i].path && /\*$/.test(aWhitelist[i].path)) {
// check for wildcard search at end
var sPathEscaped = aWhitelist[i].path.slice(0, -1).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
var rFilter = RegExp("^" + sPathEscaped);
if (rFilter.test(sPath)) {
bFound = true;
}
} else if (!aWhitelist[i].path || sPath == aWhitelist[i].path) {
// path OK
bFound = true;
}
}
}
}
if (bFound) {
break;
}
}
if (!bFound) {
return false;
}
}
return true;
};
/**
* Strips unsafe tags and attributes from HTML.
*
* @param {string} sHTML the HTML to be sanitized.
* @param {object} [mOptions={}] options for the sanitizer
* @return {string} sanitized HTML
* @private
*/
jQuery.sap._sanitizeHTML = function(sHTML, mOptions) {
return fnSanitizer(sHTML, mOptions || {
uriRewriter: function(sUrl) {
// by default we use the URL whitelist to check the URL's
if (jQuery.sap.validateUrl(sUrl)) {
return sUrl;
}
}
});
};
/**
* Registers an application defined sanitizer to be used instead of the built-in one.
*
* The given sanitizer function must have the same signature as
* {@link jQuery.sap._sanitizeHTML}:
*
* <pre>
* function sanitizer(sHtml, mOptions);
* </pre>
*
* The parameter <code>mOptions</code> will always be provided, but might be empty.
* The set of understood options is defined by the sanitizer. If no specific
* options are given, the sanitizer should run with the most secure settings.
* Sanitizers should ignore unknown settings. Known, but misconfigured settings should be
* reported as error.
*
* @param {function} fnSanitizer
* @private
*/
jQuery.sap._setHTMLSanitizer = function (fnSanitizer) {
jQuery.sap.assert(typeof fnSanitizer === "function", "Sanitizer must be a function");
fnSanitizer = fnSanitizer || defaultSanitizer;
};
function defaultSanitizer(sHTML, mOptions) {
if ( !window.html || !window.html.sanitize ) {
jQuery.sap.require("sap.ui.thirdparty.caja-html-sanitizer");
jQuery.sap.assert(window.html && window.html.sanitize, "Sanitizer should have been loaded");
}
var oTagPolicy = mOptions.tagPolicy || window.html.makeTagPolicy(mOptions.uriRewriter, mOptions.tokenPolicy);
return window.html.sanitizeWithPolicy(sHTML, oTagPolicy);
}
/**
* Globally configured sanitizer.
* @private
*/
var fnSanitizer = defaultSanitizer;
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.events.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides functionality related to eventing.
sap.ui.define(['jquery.sap.global', 'jquery.sap.keycodes'],
function(jQuery/* , jQuerySap1 */) {
"use strict";
var onTouchStart,
onTouchMove,
onTouchEnd,
onTouchCancel,
onMouseEvent,
aMouseEvents,
bIsSimulatingTouchToMouseEvent = false;
if (sap.ui.Device.browser.webkit && /Mobile/.test(navigator.userAgent) && sap.ui.Device.support.touch) {
bIsSimulatingTouchToMouseEvent = true;
(function() {
var document = window.document,
bHandleEvent = false,
oTarget = null,
bIsMoved = false,
iStartX,
iStartY,
i = 0;
aMouseEvents = ["mousedown", "mouseover", "mouseup", "mouseout", "click"];
/**
* Fires a synthetic mouse event for a given type and native touch event.
* @param {String} sType the type of the synthetic event to fire, e.g. "mousedown"
* @param {jQuery.Event} oEvent the event object
* @private
*/
var fireMouseEvent = function(sType, oEvent) {
if (!bHandleEvent) {
return;
}
// we need mapping of the different event types to get the correct target
var oMappedEvent = oEvent.type == "touchend" ? oEvent.changedTouches[0] : oEvent.touches[0];
// create the synthetic event
var newEvent = document.createEvent('MouseEvent'); // trying to create an actual TouchEvent will create an error
newEvent.initMouseEvent(sType, true, true, window, oEvent.detail,
oMappedEvent.screenX, oMappedEvent.screenY, oMappedEvent.clientX, oMappedEvent.clientY,
oEvent.ctrlKey, oEvent.shiftKey, oEvent.altKey, oEvent.metaKey,
oEvent.button, oEvent.relatedTarget);
newEvent.isSynthetic = true;
// Timeout needed. Do not interrupt the native event handling.
window.setTimeout(function() {
oTarget.dispatchEvent(newEvent);
}, 0);
};
/**
* Checks if the target of the event is an input field.
* @param {jQuery.Event} oEvent the event object
* @return {Boolean} whether the target of the event is an input field.
*/
var isInputField = function(oEvent) {
return oEvent.target.tagName.match(/input|textarea|select/i);
};
/**
* Mouse event handler. Prevents propagation for native events.
* @param {jQuery.Event} oEvent the event object
* @private
*/
onMouseEvent = function(oEvent) {
if (!oEvent.isSynthetic && !isInputField(oEvent)) {
oEvent.stopPropagation();
oEvent.preventDefault();
}
};
/**
* Touch start event handler. Called whenever a finger is added to the surface. Fires mouse start event.
* @param {jQuery.Event} oEvent the event object
* @private
*/
onTouchStart = function(oEvent) {
var oTouches = oEvent.touches,
oTouch;
bHandleEvent = (oTouches.length == 1 && !isInputField(oEvent));
bIsMoved = false;
if (bHandleEvent) {
oTouch = oTouches[0];
// As we are only interested in the first touch target, we remember it
oTarget = oTouch.target;
if (oTarget.nodeType === 3) {
// no text node
oTarget = oTarget.parentNode;
}
// Remember the start position of the first touch to determine if a click was performed or not.
iStartX = oTouch.clientX;
iStartY = oTouch.clientY;
fireMouseEvent("mousedown", oEvent);
}
};
/**
* Touch move event handler. Fires mouse move event.
* @param {jQuery.Event} oEvent the event object
* @private
*/
onTouchMove = function(oEvent) {
var oTouch;
if (bHandleEvent) {
oTouch = oEvent.touches[0];
// Check if the finger is moved. When the finger was moved, no "click" event is fired.
if (Math.abs(oTouch.clientX - iStartX) > 10 || Math.abs(oTouch.clientY - iStartY) > 10) {
bIsMoved = true;
}
if (bIsMoved) {
// Fire "mousemove" event only when the finger was moved. This is to prevent unwanted movements.
fireMouseEvent("mousemove", oEvent);
}
}
};
/**
* Touch end event handler. Fires mouse up and click event.
* @param {jQuery.Event} oEvent the event object
* @private
*/
onTouchEnd = function(oEvent) {
fireMouseEvent("mouseup", oEvent);
if (!bIsMoved) {
fireMouseEvent("click", oEvent);
}
};
/**
* Touch cancel event handler. Fires mouse up event.
* @param {jQuery.Event} oEvent the event object
* @private
*/
onTouchCancel = function(oEvent) {
fireMouseEvent("mouseup", oEvent);
};
// Bind mouse events
for (; i < aMouseEvents.length; i++) {
// Add click on capturing phase to prevent propagation if necessary
document.addEventListener(aMouseEvents[i], onMouseEvent, true);
}
// Bind touch events
document.addEventListener('touchstart', onTouchStart, true);
document.addEventListener('touchmove', onTouchMove, true);
document.addEventListener('touchend', onTouchEnd, true);
document.addEventListener('touchcancel', onTouchCancel, true);
}());
}
/**
* Disable touch to mouse handling
*
* @public
*/
jQuery.sap.disableTouchToMouseHandling = function() {
var i = 0;
if (!bIsSimulatingTouchToMouseEvent) {
return;
}
// unbind touch events
document.removeEventListener('touchstart', onTouchStart, true);
document.removeEventListener('touchmove', onTouchMove, true);
document.removeEventListener('touchend', onTouchEnd, true);
document.removeEventListener('touchcancel', onTouchCancel, true);
// unbind mouse events
for (; i < aMouseEvents.length; i++) {
document.removeEventListener(aMouseEvents[i], onMouseEvent, true);
}
};
/**
* List of DOM events that a UIArea automatically takes care of.
*
* A control/element doesn't have to bind listeners for these events.
* It instead can implement an <code>on<i>event</i>(oEvent)</code> method
* for any of the following events that it wants to be notified about:
*
* click, dblclick, contextmenu, focusin, focusout, keydown, keypress, keyup, mousedown, mouseout, mouseover,
* mouseup, select, selectstart, dragstart, dragenter, dragover, dragleave, dragend, drop, paste, cut, input
*
* In case touch events are natively supported the following events are available in addition:
* touchstart, touchend, touchmove, touchcancel
*
* @public
*/
jQuery.sap.ControlEvents = [ // IMPORTANT: update the public documentation when extending this list
"click",
"dblclick",
"contextmenu",
"focusin",
"focusout",
"keydown",
"keypress",
"keyup",
"mousedown",
"mouseout",
"mouseover",
"mouseup",
"select",
"selectstart",
"dragstart",
"dragenter",
"dragover",
"dragleave",
"dragend",
"drop",
"paste",
"cut",
/* input event is fired synchronously on IE9+ when the value of an <input> or <textarea> element is changed */
/* for more details please see : https://developer.mozilla.org/en-US/docs/Web/Reference/Events/input */
"input"
];
// touch events natively supported
if (sap.ui.Device.support.touch) {
// Define additional native events to be added to the event list.
// TODO: maybe add "gesturestart", "gesturechange", "gestureend" later?
jQuery.sap.ControlEvents.push("touchstart", "touchend", "touchmove", "touchcancel");
}
/**
* Enumeration of all so called "pseudo events", a useful classification
* of standard browser events as implied by SAP product standards.
*
* Whenever a browser event is recognized as one or more pseudo events, then this
* classification is attached to the original {@link jQuery.Event} object and thereby
* delivered to any jQuery-style listeners registered for that browser event.
*
* Pure JavaScript listeners can evaluate the classification information using
* the {@link jQuery.Event#isPseudoType} method.
*
* Instead of using the procedure as described above, the SAPUI5 controls and elements
* should simply implement an <code>on<i>pseudo-event</i>(oEvent)</code> method. It will
* be invoked only when that specific pseudo event has been recognized. This simplifies event
* dispatching even further.
*
* @namespace
* @public
*/
jQuery.sap.PseudoEvents = { // IMPORTANT: update the public documentation when extending this list
/* Pseudo keyboard events */
/**
* Pseudo event for keyboard arrow down without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdown: {sName: "sapdown", aTypes: ["keydown"], fnCheck: function (oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard arrow down with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdownmodifiers: {sName: "sapdownmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'show' event (F4, Alt + down-Arrow)
* @public
*/
sapshow: {sName: "sapshow", aTypes: ["keydown"], fnCheck: function(oEvent) {
return (oEvent.keyCode == jQuery.sap.KeyCodes.F4 && !hasModifierKeys(oEvent)) ||
(oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN && checkModifierKeys(oEvent, /*Ctrl*/false, /*Alt*/true, /*Shift*/false));
}},
/**
* Pseudo event for keyboard arrow up without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapup: {sName: "sapup", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard arrow up with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapupmodifiers: {sName: "sapupmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'hide' event (Alt + up-Arrow)
* @public
*/
saphide: {sName: "saphide", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP && checkModifierKeys(oEvent, /*Ctrl*/false, /*Alt*/true, /*Shift*/false);
}},
/**
* Pseudo event for keyboard arrow left without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapleft: {sName: "sapleft", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_LEFT && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard arrow left with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapleftmodifiers: {sName: "sapleftmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_LEFT && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard arrow right without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapright: {sName: "sapright", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_RIGHT && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard arrow right with modifiers (Ctrl, Alt or Shift)
* @public
*/
saprightmodifiers: {sName: "saprightmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_RIGHT && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard Home/Pos1 with modifiers (Ctrl, Alt or Shift)
* @public
*/
saphome: {sName: "saphome", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.HOME && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard Home/Pos1 without modifiers (Ctrl, Alt or Shift)
* @public
*/
saphomemodifiers: {sName: "saphomemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.HOME && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo top event
* @public
*/
saptop: {sName: "saptop", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.HOME && checkModifierKeys(oEvent, /*Ctrl*/true, /*Alt*/false, /*Shift*/false);
}},
/**
* Pseudo event for keyboard End without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapend: {sName: "sapend", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.END && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard End with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapendmodifiers: {sName: "sapendmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.END && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo bottom event
* @public
*/
sapbottom: {sName: "sapbottom", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.END && checkModifierKeys(oEvent, /*Ctrl*/true, /*Alt*/false, /*Shift*/false);
}},
/**
* Pseudo event for keyboard page up without modifiers (Ctrl, Alt or Shift)
* @public
*/
sappageup: {sName: "sappageup", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.PAGE_UP && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard page up with modifiers (Ctrl, Alt or Shift)
* @public
*/
sappageupmodifiers: {sName: "sappageupmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.PAGE_UP && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard page down without modifiers (Ctrl, Alt or Shift)
* @public
*/
sappagedown: {sName: "sappagedown", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.PAGE_DOWN && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard page down with modifiers (Ctrl, Alt or Shift)
* @public
*/
sappagedownmodifiers: {sName: "sappagedownmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.PAGE_DOWN && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'select' event... space, enter, ... without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapselect: {sName: "sapselect", aTypes: ["keydown"], fnCheck: function(oEvent) {
return (oEvent.keyCode == jQuery.sap.KeyCodes.ENTER || oEvent.keyCode == jQuery.sap.KeyCodes.SPACE) && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'select' event... space, enter, ... with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapselectmodifiers: {sName: "sapselectmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return (oEvent.keyCode == jQuery.sap.KeyCodes.ENTER || oEvent.keyCode == jQuery.sap.KeyCodes.SPACE) && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard space without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapspace: {sName: "sapspace", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.SPACE && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard space with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapspacemodifiers: {sName: "sapspacemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.SPACE && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard enter without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapenter: {sName: "sapenter", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ENTER && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard enter with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapentermodifiers: {sName: "sapentermodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ENTER && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard backspace without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapbackspace: {sName: "sapbackspace", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.BACKSPACE && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard backspace with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapbackspacemodifiers: {sName: "sapbackspacemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.BACKSPACE && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard delete without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdelete: {sName: "sapdelete", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.DELETE && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard delete with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdeletemodifiers: {sName: "sapdeletemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.DELETE && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo expand event (keyboard numpad +) without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapexpand: {sName: "sapexpand", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.NUMPAD_PLUS && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo expand event (keyboard numpad +) with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapexpandmodifiers: {sName: "sapexpandmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.NUMPAD_PLUS && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo collapse event (keyboard numpad -) without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapcollapse: {sName: "sapcollapse", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.NUMPAD_MINUS && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo collapse event (keyboard numpad -) with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapcollapsemodifiers: {sName: "sapcollapsemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.NUMPAD_MINUS && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo collapse event (keyboard numpad *)
* @public
*/
sapcollapseall: {sName: "sapcollapseall", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.NUMPAD_ASTERISK && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard escape
* @public
*/
sapescape: {sName: "sapescape", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.ESCAPE && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard tab (TAB + no modifier)
* @public
*/
saptabnext: {sName: "saptabnext", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.TAB && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for keyboard tab (TAB + shift modifier)
* @public
*/
saptabprevious: {sName: "saptabprevious", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.TAB && checkModifierKeys(oEvent, /*Ctrl*/false, /*Alt*/false, /*Shift*/true);
}},
/**
* Pseudo event for pseudo skip forward (F6 + no modifier)
* @public
*/
sapskipforward: {sName: "sapskipforward", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.F6 && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo skip back (F6 + shift modifier)
* @public
*/
sapskipback: {sName: "sapskipback", aTypes: ["keydown"], fnCheck: function(oEvent) {
return oEvent.keyCode == jQuery.sap.KeyCodes.F6 && checkModifierKeys(oEvent, /*Ctrl*/false, /*Alt*/false, /*Shift*/true);
}},
//// contextmenu Shift-F10 hack
//{sName: "sapcontextmenu", aTypes: ["keydown"], fnCheck: function(oEvent) {
// return oEvent.keyCode == jQuery.sap.KeyCodes.F10 && checkModifierKeys(oEvent, /*Ctrl*/false, /*Alt*/false, /*Shift*/true);
//}},
/**
* Pseudo event for pseudo 'decrease' event without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdecrease: {sName: "sapdecrease", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iPreviousKey = bRtl ? jQuery.sap.KeyCodes.ARROW_RIGHT : jQuery.sap.KeyCodes.ARROW_LEFT;
return (oEvent.keyCode == iPreviousKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN) && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pressing the '-' (minus) sign.
* @since 1.25.0
* @experimental Since 1.25.0 Implementation details can be changed in future.
* @public
*/
sapminus: {sName: "sapminus", aTypes: ["keypress"], fnCheck: function(oEvent) {
var sCharCode = String.fromCharCode(oEvent.which);
return sCharCode == '-';
}},
/**
* Pseudo event for pseudo 'decrease' event with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapdecreasemodifiers: {sName: "sapdecreasemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iPreviousKey = bRtl ? jQuery.sap.KeyCodes.ARROW_RIGHT : jQuery.sap.KeyCodes.ARROW_LEFT;
return (oEvent.keyCode == iPreviousKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN) && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'increase' event without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapincrease: {sName: "sapincrease", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iNextKey = bRtl ? jQuery.sap.KeyCodes.ARROW_LEFT : jQuery.sap.KeyCodes.ARROW_RIGHT;
return (oEvent.keyCode == iNextKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP) && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pressing the '+' (plus) sign.
* @since 1.25.0
* @experimental Since 1.25.0 Implementation details can be changed in future.
* @public
*/
sapplus: {sName: "sapplus", aTypes: ["keypress"], fnCheck: function(oEvent) {
var sCharCode = String.fromCharCode(oEvent.which);
return sCharCode == '+';
}},
/**
* Pseudo event for pseudo 'increase' event with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapincreasemodifiers: {sName: "sapincreasemodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iNextKey = bRtl ? jQuery.sap.KeyCodes.ARROW_LEFT : jQuery.sap.KeyCodes.ARROW_RIGHT;
return (oEvent.keyCode == iNextKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP) && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'previous' event without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapprevious: {sName: "sapprevious", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iPreviousKey = bRtl ? jQuery.sap.KeyCodes.ARROW_RIGHT : jQuery.sap.KeyCodes.ARROW_LEFT;
return (oEvent.keyCode == iPreviousKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP) && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'previous' event with modifiers (Ctrl, Alt or Shift)
* @public
*/
sappreviousmodifiers: {sName: "sappreviousmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iPreviousKey = bRtl ? jQuery.sap.KeyCodes.ARROW_RIGHT : jQuery.sap.KeyCodes.ARROW_LEFT;
return (oEvent.keyCode == iPreviousKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_UP) && hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'next' event without modifiers (Ctrl, Alt or Shift)
* @public
*/
sapnext: {sName: "sapnext", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iNextKey = bRtl ? jQuery.sap.KeyCodes.ARROW_LEFT : jQuery.sap.KeyCodes.ARROW_RIGHT;
return (oEvent.keyCode == iNextKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN) && !hasModifierKeys(oEvent);
}},
/**
* Pseudo event for pseudo 'next' event with modifiers (Ctrl, Alt or Shift)
* @public
*/
sapnextmodifiers: {sName: "sapnextmodifiers", aTypes: ["keydown"], fnCheck: function(oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
var iNextKey = bRtl ? jQuery.sap.KeyCodes.ARROW_LEFT : jQuery.sap.KeyCodes.ARROW_RIGHT;
return (oEvent.keyCode == iNextKey || oEvent.keyCode == jQuery.sap.KeyCodes.ARROW_DOWN) && hasModifierKeys(oEvent);
}},
//// pseudo hotkey event
//{sName: "saphotkey", aTypes: ["keydown"], fnCheck: function(oEvent) {
// return false;
//}},
/* TODO: hotkeys: all other events could be hotkeys
if(UCF_KeyboardHelper.bIsValidHotkey(iKey, bCtrl, bAlt, bShift)) {
if (iKey == jQuery.sap.KeyCodes.F1 && bNoModifiers) {
//special handling for FF - in IE the help is handeled by onHelp
if (UCF_System.sDevice == "ff1") {
this.fireSapEvent(this.E_SAP_EVENTS.hotkey, oEvent);
}
}
else if (bCtrlOnly && iKey == jQuery.sap.KeyCodes.C && document.selection) {
//handle ctrl+c centrally if text is selected to allow to copy it instead of firing the hotkey
var oTextRange = document.selection.createRange();
if (!oTextRange || oTextRange.text.length <= 0) {
this.fireSapEvent(this.E_SAP_EVENTS.hotkey, oEvent);
}
}
else {
this.fireSapEvent(this.E_SAP_EVENTS.hotkey, oEvent);
}
}
*/
/*
* Other pseudo events
* @public
*/
/**
* Pseudo event indicating delayed double click (e.g. for inline edit)
* @public
*/
sapdelayeddoubleclick: {sName: "sapdelayeddoubleclick", aTypes: ["click"], fnCheck: function(oEvent) {
var element = jQuery(oEvent.target);
var currentTimestamp = oEvent.timeStamp;
var data = element.data("sapdelayeddoubleclick_lastClickTimestamp");
var lastTimestamp = data || 0;
element.data("sapdelayeddoubleclick_lastClickTimestamp", currentTimestamp);
var diff = currentTimestamp - lastTimestamp;
return (diff >= 300 && diff <= 1300);
}}
};
/**
* Ordered array of the {@link jQuery.sap.PseudoEvents}.
*
* Order is significant as some check methods rely on the fact that they are tested before other methods.
* The array is processed during event analysis (when classifying browser events as pseudo events).
* @private
*/
var PSEUDO_EVENTS = ["sapdown", "sapdownmodifiers", "sapshow", "sapup", "sapupmodifiers", "saphide", "sapleft", "sapleftmodifiers", "sapright", "saprightmodifiers", "saphome", "saphomemodifiers", "saptop", "sapend", "sapendmodifiers", "sapbottom", "sappageup", "sappageupmodifiers", "sappagedown", "sappagedownmodifiers", "sapselect", "sapselectmodifiers", "sapspace", "sapspacemodifiers", "sapenter", "sapentermodifiers", "sapexpand", "sapbackspace", "sapbackspacemodifiers", "sapdelete", "sapdeletemodifiers", "sapexpandmodifiers", "sapcollapse", "sapcollapsemodifiers", "sapcollapseall", "sapescape", "saptabnext", "saptabprevious", "sapskipforward", "sapskipback", "sapprevious", "sappreviousmodifiers", "sapnext", "sapnextmodifiers", "sapdecrease", "sapminus", "sapdecreasemodifiers", "sapincrease", "sapplus", "sapincreasemodifiers", "sapdelayeddoubleclick"];
//Add mobile touch events if touch is supported
(function initTouchEventSupport() {
jQuery.sap.touchEventMode = "SIM";
var aAdditionalControlEvents = [];
var aAdditionalPseudoEvents = [];
if (sap.ui.Device.support.touch) { // touch events natively supported
jQuery.sap.touchEventMode = "ON";
// ensure that "oEvent.touches", ... works (and not only "oEvent.originalEvent.touches", ...)
jQuery.event.props.push("touches", "targetTouches", "changedTouches");
}
/**
* This function adds the simulated event prefixed with string "sap" to jQuery.sap.ControlEvents.
*
* When UIArea binds to the simulated event with prefix, it internally binds to the original events with the given handler and
* also provides the additional configuration data in the follwing format:
*
* {
* domRef: // the dom reference of the UIArea
* eventName: // the simulated event name
* sapEventName: // the simulated event name with sap prefix
* eventHandle: // the handler that should be registered to simulated event with sap prefix
* }
*
* @param {string} sSimEventName The name of the simulated event
* @param {array} aOrigEvents The array of original events that should be simulated from
* @param {function} fnHandler The function which is bound to the original events
*
* @private
*/
var createSimulatedEvent = function(sSimEventName, aOrigEvents, fnHandler) {
var sHandlerKey = "__" + sSimEventName + "Handler";
var sSapSimEventName = "sap" + sSimEventName;
aAdditionalControlEvents.push(sSapSimEventName);
aAdditionalPseudoEvents.push({sName: sSimEventName, aTypes: [sSapSimEventName], fnCheck: function (oEvent) {
return true;
}});
jQuery.event.special[sSapSimEventName] = {
// When binding to the simulated event with prefix is done through jQuery, this function is called and redirect the registration
// to the original events. Doing in this way we can simulate the event from listening to the original events.
add: function(oHandle) {
var that = this,
$this = jQuery(this),
oAdditionalConfig = {
domRef: that,
eventName: sSimEventName,
sapEventName: sSapSimEventName,
eventHandle: oHandle
};
var fnHandlerWrapper = function(oEvent){
fnHandler(oEvent, oAdditionalConfig);
};
oHandle.__sapSimulatedEventHandler = fnHandlerWrapper;
for (var i = 0; i < aOrigEvents.length; i++) {
$this.on(aOrigEvents[i], fnHandlerWrapper);
}
},
// When unbinding to the simulated event with prefix is done through jQuery, this function is called and redirect the deregistration
// to the original events.
remove: function(oHandle) {
var $this = jQuery(this);
var fnHandler = oHandle.__sapSimulatedEventHandler;
$this.removeData(sHandlerKey + oHandle.guid);
for (var i = 0; i < aOrigEvents.length; i++) {
jQuery.event.remove(this, aOrigEvents[i], fnHandler);
}
}
};
};
/**
* This function simulates the corresponding touch event by listening to mouse event.
*
* The simulated event will be dispatch through UI5 event delegation which means that the on"EventName" function is called
* on control's prototype.
*
* @param {jQuery.Event} oEvent The original event object
* @param {object} oConfig Additional configuration passed from createSimulatedEvent function
* @private
*/
var fnMouseToTouchHandler = function(oEvent, oConfig) {
var $DomRef = jQuery(oConfig.domRef);
// Suppress the delayed mouse events simulated on touch enabled device
// the mark is done within jquery-mobile-custom.js
if (oEvent.isMarked("delayedMouseEvent")) {
return;
}
// Checks if the mouseout event should be handled, the mouseout of the inner dom shouldn't be handled when the mouse cursor
// is still inside the control's root dom node
if (!(oEvent.type != "mouseout" || (oEvent.type === "mouseout" && jQuery.sap.checkMouseEnterOrLeave(oEvent, oConfig.domRef)))) {
var bSkip = true;
var sControlId = $DomRef.data("__touchstart_control");
if (sControlId) {
var oCtrlDom = jQuery.sap.domById(sControlId);
if (oCtrlDom && jQuery.sap.checkMouseEnterOrLeave(oEvent, oCtrlDom)) {
bSkip = false;
}
}
if (bSkip) {
return;
}
}
var oNewEvent = jQuery.event.fix(oEvent.originalEvent || oEvent);
oNewEvent.type = oConfig.sapEventName;
//reset the _sapui_handledByUIArea flag
if (oNewEvent.isMarked("firstUIArea")) {
oNewEvent.setMark("handledByUIArea", false);
}
var aTouches = [{
identifier: 1,
pageX: oNewEvent.pageX,
pageY: oNewEvent.pageY,
clientX: oNewEvent.clientX,
clientY: oNewEvent.clientY,
screenX: oNewEvent.screenX,
screenY: oNewEvent.screenY,
target: oNewEvent.target,
radiusX: 1,
radiusY: 1,
rotationAngle: 0
}];
switch (oConfig.eventName) {
case "touchstart":
case "touchmove":
oNewEvent.touches = oNewEvent.changedTouches = oNewEvent.targetTouches = aTouches;
break;
case "touchend":
oNewEvent.changedTouches = aTouches;
oNewEvent.touches = oNewEvent.targetTouches = [];
break;
// no default
}
if (oConfig.eventName === "touchstart" || $DomRef.data("__touch_in_progress")) {
$DomRef.data("__touch_in_progress", "X");
var oControl = jQuery.fn.control ? jQuery(oEvent.target).control(0) : null;
if (oControl) {
$DomRef.data("__touchstart_control", oControl.getId());
}
// When saptouchend event is generated from mouseout event, it has to be marked for being correctly handled inside UIArea.
// for example, when sap.m.Image control is used inside sap.m.Button control, the following situation can happen:
// 1. Mousedown on image.
// 2. Keep mousedown and move mouse out of image.
// 3. ontouchend function will be called on image control and bubbled up to button control
// 4. However, the ontouchend function shouldn't be called on button.
//
// With this parameter, UIArea can check if the touchend is generated from mouseout event and check if the target is still
// inside the current target. Executing the corresponding logic only when the target is out of the current target.
if (oEvent.type === "mouseout") {
oNewEvent.setMarked("fromMouseout");
}
oConfig.eventHandle.handler.call(oConfig.domRef, oNewEvent);
// here the fromMouseout flag is checked, terminate the touch progress only when touchend event is not marked with fromMouseout.
if (oConfig.eventName === "touchend" && !oNewEvent.isMarked("fromMouseout")) {
$DomRef.removeData("__touch_in_progress");
$DomRef.removeData("__touchstart_control");
}
}
};
if (!(sap.ui.Device.support.pointer && sap.ui.Device.support.touch)) {
createSimulatedEvent("touchstart", ["mousedown"], fnMouseToTouchHandler);
createSimulatedEvent("touchend", ["mouseup", "mouseout"], fnMouseToTouchHandler);
createSimulatedEvent("touchmove", ["mousemove"], fnMouseToTouchHandler);
}
/**
* This methods decides when extra events are needed. Extra events are: tap, swipe and the new touch to mouse event simulation.
*
* The old touch to mouse simulation is done in a way that a real mouse event is fired when there's a corresponding touch event. But this will mess up
* the mouse to touch event simulation and is not consistent with the mouse to touch event simulation. That's why when certain condition is met, the old
* touch to mouse event simluation will be replaced with the new touch to mouse event simulation.
*
* The new one can't completely replace the old one because the desktop controls which bind to events using jQuery or browser API directly have to be change.
* Then the new one can replace the old one completely not under certain condition anymore.
*
* @private
*/
function needsExtraEventSupport(){
var oCfgData = window["sap-ui-config"] || {},
sLibs = oCfgData.libs || "";
// TODO: should be replaced by some function in jQuery.sap.global (e.g. jQuery.sap.config(sKey))
function hasConfig(sKey) {
return document.location.search.indexOf("sap-ui-" + sKey) > -1 || // URL
!!oCfgData[sKey.toLowerCase()]; // currently, properties of oCfgData are converted to lower case (DOM attributes)
}
return sap.ui.Device.support.touch || // tap, swipe, etc. events are needed when touch is supported
hasConfig("xx-test-mobile") || // see sap.ui.core.Configuration -> M_SETTINGS
// also simulate touch events when sap-ui-xx-fakeOS is set (independently of the value and the current browser)
hasConfig("xx-fakeOS") ||
// always simulate touch events when the mobile lib is involved (FIXME: hack for Kelley, this does currently not work with dynamic library loading)
sLibs.match(/sap.m\b/);
}
// If extra event support is needed, jQuery mobile event plugin is loaded to support tap, swipe and scrollstart/stop events.
// The old touch to mouse event simulation ((see line 25 in this file)) will be deregistered and the new one will be active.
if (needsExtraEventSupport()) {
jQuery.sap.require("sap.ui.thirdparty.jquery-mobile-custom");
// Simulate mouse events on touch devices
// Except for Windows Phone with touch events support.
if (sap.ui.Device.support.touch && !sap.ui.Device.support.pointer) {
var bFingerIsMoved = false,
iMoveThreshold = jQuery.vmouse.moveDistanceThreshold,
iStartX, iStartY,
iOffsetX, iOffsetY;
/**
* This function simulates the corresponding mouse event by listening to touch event.
*
* The simulated event will be dispatch through UI5 event delegation which means that the on"EventName" function is called
* on control's prototype.
*
* @param {jQuery.Event} oEvent The original event object
* @param {object} oConfig Additional configuration passed from createSimulatedEvent function
*/
var fnTouchToMouseHandler = function(oEvent, oConfig) {
var oTouch = oEvent.originalEvent.touches[0],
bEventHandledByUIArea;
if (oEvent.type === "touchstart") {
bFingerIsMoved = false;
iStartX = oTouch.pageX;
iStartY = oTouch.pageY;
iOffsetX = Math.round(oTouch.pageX - jQuery(oEvent.target).offset().left);
iOffsetY = Math.round(oTouch.pageY - jQuery(oEvent.target).offset().top);
} else if (oEvent.type === "touchmove") {
bFingerIsMoved = bFingerIsMoved ||
(Math.abs(oTouch.pageX - iStartX) > iMoveThreshold ||
Math.abs(oTouch.pageY - iStartY) > iMoveThreshold);
}
var oNewEvent = jQuery.event.fix(oEvent.originalEvent || oEvent);
oNewEvent.type = oConfig.sapEventName;
//reset the _sapui_handledByUIArea flag
if (oNewEvent.isMarked("firstUIArea")) {
oNewEvent.setMark("handledByUIArea", false);
}
delete oNewEvent.touches;
delete oNewEvent.changedTouches;
delete oNewEvent.targetTouches;
var oMappedEvent = (oConfig.eventName === "mouseup" ? oEvent.changedTouches[0] : oEvent.touches[0]);
//TODO: add other properties that should be copied to the new event
oNewEvent.screenX = oMappedEvent.screenX;
oNewEvent.screenY = oMappedEvent.screenY;
oNewEvent.clientX = oMappedEvent.clientX;
oNewEvent.clientY = oMappedEvent.clientY;
oNewEvent.ctrlKey = oMappedEvent.ctrlKey;
oNewEvent.altKey = oMappedEvent.altKey;
oNewEvent.shiftKey = oMappedEvent.shiftKey;
// The simulated mouse event should always be clicked by the left key of the mouse
oNewEvent.button = (sap.ui.Device.browser.msie && sap.ui.Device.browser.version <= 8 ? 1 : 0);
bEventHandledByUIArea = oNewEvent.isMarked("handledByUIArea");
oConfig.eventHandle.handler.call(oConfig.domRef, oNewEvent);
// also call the onclick event handler when touchend event is received and the movement is within threshold
if (oEvent.type === "touchend" && !bEventHandledByUIArea && !bFingerIsMoved) {
oNewEvent.type = "click";
oNewEvent.setMark("handledByUIArea", false);
oNewEvent.offsetX = iOffsetX; // use offset from touchstart
oNewEvent.offsetY = iOffsetY; // use offset from touchstart
oConfig.eventHandle.handler.call(oConfig.domRef, oNewEvent);
}
};
// Deregister the previous touch to mouse event simulation (see line 25 in this file)
jQuery.sap.disableTouchToMouseHandling();
createSimulatedEvent("mousedown", ["touchstart"], fnTouchToMouseHandler);
createSimulatedEvent("mousemove", ["touchmove"], fnTouchToMouseHandler);
createSimulatedEvent("mouseup", ["touchend", "touchcancel"], fnTouchToMouseHandler);
}
// Define additional jQuery Mobile events to be added to the event list
// TODO taphold cannot be used (does not bubble / has no target property) -> Maybe provide own solution
// IMPORTANT: update the public documentation when extending this list
aAdditionalControlEvents.push("swipe", "tap", "swipeleft", "swiperight", "scrollstart", "scrollstop");
//Define additional pseudo events to be added to the event list
aAdditionalPseudoEvents.push({sName: "swipebegin", aTypes: ["swipeleft", "swiperight"], fnCheck: function (oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
return (bRtl && oEvent.type === "swiperight") || (!bRtl && oEvent.type === "swipeleft");
}});
aAdditionalPseudoEvents.push({sName: "swipeend", aTypes: ["swipeleft", "swiperight"], fnCheck: function (oEvent) {
var bRtl = sap.ui.getCore().getConfiguration().getRTL();
return (!bRtl && oEvent.type === "swiperight") || (bRtl && oEvent.type === "swipeleft");
}});
}
// Add all defined events to the event infrastructure
//
// jQuery has inversed the order of event registration when multiple events are passed into jQuery.on method from version 1.9.1.
//
// UIArea binds to both touchstart and saptouchstart event and saptouchstart internally also binds to touchstart event. Before
// jQuery version 1.9.1, the touchstart event handler is called before the saptouchstart event handler and our flags (e.g. _sapui_handledByUIArea)
// still work. However since the order of event registration is inversed from jQuery version 1.9.1, the saptouchstart event hanlder is called
// before the touchstart one, our flags don't work anymore.
//
// Therefore jQuery version needs to be checked in order to decide the event order in jQuery.sap.ControlEvents.
if (jQuery.sap.Version(jQuery.fn.jquery).compareTo("1.9.1") < 0) {
jQuery.sap.ControlEvents = jQuery.sap.ControlEvents.concat(aAdditionalControlEvents);
} else {
jQuery.sap.ControlEvents = aAdditionalControlEvents.concat(jQuery.sap.ControlEvents);
}
for (var i = 0; i < aAdditionalPseudoEvents.length; i++) {
jQuery.sap.PseudoEvents[aAdditionalPseudoEvents[i].sName] = aAdditionalPseudoEvents[i];
PSEUDO_EVENTS.push(aAdditionalPseudoEvents[i].sName);
}
}());
/**
* Function for initialization of an Array containing all basic event types of the available pseudo events.
* @private
*/
function initPseudoEventBasicTypes(){
var mEvents = jQuery.sap.PseudoEvents,
aResult = [];
for (var sName in mEvents) {
if (mEvents[sName].aTypes) {
for (var j = 0, js = mEvents[sName].aTypes.length; j < js; j++) {
var sType = mEvents[sName].aTypes[j];
if (jQuery.inArray(sType, aResult) == -1) {
aResult.push(sType);
}
}
}
}
return aResult;
}
/**
* Array containing all basic event types of the available pseudo events.
* @private
*/
var PSEUDO_EVENTS_BASIC_TYPES = initPseudoEventBasicTypes();
/**
* Convenience method to check an event for a certain combination of modifier keys
*
* @private
*/
function checkModifierKeys(oEvent, bCtrlKey, bAltKey, bShiftKey) {
return oEvent.shiftKey == bShiftKey && oEvent.altKey == bAltKey && getCtrlKey(oEvent) == bCtrlKey;
}
/**
* Convenience method to check an event for any modifier key
*
* @private
*/
function hasModifierKeys(oEvent) {
return oEvent.shiftKey || oEvent.altKey || getCtrlKey(oEvent);
}
/**
* Convenience method for handling of Ctrl key, meta key etc.
*
* @private
*/
function getCtrlKey(oEvent) {
return !!(oEvent.metaKey || oEvent.ctrlKey); // double negation doesn't have effect on boolean but ensures null and undefined are equivalent to false.
}
/**
* Returns an array of names (as strings) identifying {@link jQuery.sap.PseudoEvents} that are fulfilled by this very Event instance.
*
* @returns {String[]} Array of names identifying {@link jQuery.sap.PseudoEvents} that are fulfilled by this very Event instance.
* @public
*/
jQuery.Event.prototype.getPseudoTypes = function() {
var aPseudoTypes = [];
if (jQuery.inArray(this.type, PSEUDO_EVENTS_BASIC_TYPES) != -1) {
var aPseudoEvents = PSEUDO_EVENTS;
var ilength = aPseudoEvents.length;
var oPseudo = null;
for (var i = 0; i < ilength; i++) {
oPseudo = jQuery.sap.PseudoEvents[aPseudoEvents[i]];
if (oPseudo.aTypes
&& jQuery.inArray(this.type, oPseudo.aTypes) > -1
&& oPseudo.fnCheck
&& oPseudo.fnCheck(this)) {
aPseudoTypes.push(oPseudo.sName);
}
}
}
this.getPseudoTypes = function(){
return aPseudoTypes.slice();
};
return aPseudoTypes.slice();
};
/**
* Checks whether this instance of {@link jQuery.Event} is of the given <code>sType</code> pseudo type.
*
* @param {string} sType The name of the pseudo type this event should be checked for.
* @returns {boolean} <code>true</code> if this instance of jQuery.Event is of the given sType, <code>false</code> otherwise.
* @public
*/
jQuery.Event.prototype.isPseudoType = function(sType) {
var aPseudoTypes = this.getPseudoTypes();
if (sType) {
return jQuery.inArray(sType, aPseudoTypes) > -1;
} else {
return aPseudoTypes.length > 0;
}
};
/*
* store reference to original preventDefault method
*/
var _preventDefault = jQuery.Event.prototype.preventDefault;
/*
* and introduce some keyCode fixing for IE...
* this e.g. suppresses the address-field drop down opening in case of sapshow (i.e. F4) in ComboBoxes
*/
jQuery.Event.prototype.preventDefault = function() {
_preventDefault.apply(this, arguments);
var e = this.originalEvent;
if ( !e ) {
return;
}
if ( e.keyCode != 0 ) {
try { // Sometimes setting keycode results in "Access Denied"
if (!sap.ui.Device.browser.firefox) {
e.keyCode = 0;
}
} catch (ex) {}
}
};
/**
* Binds all events for listening with the given callback function.
*
* @param {function} fnCallback Callback function
* @public
*/
jQuery.sap.bindAnyEvent = function bindAnyEvent(fnCallback) {
if (fnCallback) {
jQuery(document).bind(jQuery.sap.ControlEvents.join(" "), fnCallback);
}
};
/**
* Unbinds all events for listening with the given callback function.
*
* @param {function} fnCallback Callback function
* @public
*/
jQuery.sap.unbindAnyEvent = function unbindAnyEvent(fnCallback) {
if (fnCallback) {
jQuery(document).unbind(jQuery.sap.ControlEvents.join(" "), fnCallback);
}
};
/**
* Checks a given mouseover or mouseout event whether it is
* equivalent to a mouseenter or mousleave event regarding the given DOM reference.
*
* @param {jQuery.Event} oEvent
* @param {element} oDomRef
* @public
*/
jQuery.sap.checkMouseEnterOrLeave = function checkMouseEnterOrLeave(oEvent, oDomRef) {
if (oEvent.type != "mouseover" && oEvent.type != "mouseout") {
return false;
}
var isMouseEnterLeave = false;
var element = oDomRef;
var parent = oEvent.relatedTarget;
try {
while ( parent && parent !== element ) {
parent = parent.parentNode;
}
if ( parent !== element ) {
isMouseEnterLeave = true;
}
} catch (e) { }
return isMouseEnterLeave;
};
/*
* Detect whether the pressed key is:
* SHIFT, CONTROL, ALT, BREAK, CAPS_LOCK,
* PAGE_UP, PAGE_DOWN, END, HOME, ARROW_LEFT, ARROW_UP, ARROW_RIGHT, ARROW_DOWN,
* PRINT, INSERT, DELETE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
* BACKSPACE, TAB, ENTER, ESCAPE
*
* @param {jQuery.Event} oEvent The event object of the <code>keydown</code>, <code>keyup</code> or <code>keypress</code> events.
* @static
* @returns {boolean}
* @protected
* @since 1.24.0
* @experimental Since 1.24.0 Implementation might change.
*/
jQuery.sap.isSpecialKey = function(oEvent) {
var mKeyCodes = jQuery.sap.KeyCodes,
iKeyCode = oEvent.which, // jQuery oEvent.which normalizes oEvent.keyCode and oEvent.charCode
bSpecialKey = isModifierKey(oEvent) ||
isArrowKey(oEvent) ||
(iKeyCode >= 33 && iKeyCode <= 36) || // PAGE_UP, PAGE_DOWN, END, HOME
(iKeyCode >= 44 && iKeyCode <= 46) || // PRINT, INSERT, DELETE
(iKeyCode >= 112 && iKeyCode <= 123) || // F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12
(iKeyCode === mKeyCodes.BREAK) ||
(iKeyCode === mKeyCodes.BACKSPACE) ||
(iKeyCode === mKeyCodes.TAB) ||
(iKeyCode === mKeyCodes.ENTER) ||
(iKeyCode === mKeyCodes.ESCAPE) ||
(iKeyCode === mKeyCodes.SCROLL_LOCK);
switch (oEvent.type) {
case "keydown":
case "keyup":
return bSpecialKey;
// note: the keypress event should be fired only when a character key is pressed,
// unfortunately some browsers fire the keypress event for other keys. e.g.:
//
// Firefox fire it for:
// BREAK, ARROW_LEFT, ARROW_RIGHT, INSERT, DELETE,
// F1, F2, F3, F5, F6, F7, F8, F9, F10, F11, F12
// BACKSPACE, ESCAPE
//
// Internet Explorer fire it for:
// ESCAPE
case "keypress":
// note: in Firefox, almost all noncharacter keys that fire the keypress event have a key code of 0,
// with the exception of BACKSPACE (key code of 8).
// note: in IE the ESCAPE key is also fired for the the keypress event
return (iKeyCode === 0 || // in Firefox, almost all noncharacter keys that fire the keypress event have a key code of 0, with the exception of BACKSPACE (key code of 8)
iKeyCode === mKeyCodes.BACKSPACE ||
iKeyCode === mKeyCodes.ESCAPE ||
iKeyCode === mKeyCodes.ENTER /* all browsers */) || false;
default:
return false;
}
};
/**
* Detect whether the pressed key is a modifier.
*
* Modifier keys are considered:
* SHIFT, CONTROL, ALT, CAPS_LOCK, NUM_LOCK
* These keys don't send characters, but modify the characters sent by other keys.
*
* @param {jQuery.Event} oEvent The event object of the <code>keydown</code>, <code>keyup</code> or <code>keypress</code> events.
* @static
* @returns {boolean}
* @since 1.24.0
*/
function isModifierKey(oEvent) {
var mKeyCodes = jQuery.sap.KeyCodes,
iKeyCode = oEvent.which; // jQuery oEvent.which normalizes oEvent.keyCode and oEvent.charCode
return (iKeyCode === mKeyCodes.SHIFT) ||
(iKeyCode === mKeyCodes.CONTROL) ||
(iKeyCode === mKeyCodes.ALT) ||
(iKeyCode === mKeyCodes.CAPS_LOCK) ||
(iKeyCode === mKeyCodes.NUM_LOCK);
}
/**
* Detect whether the pressed key is a navigation key.
*
* Navigation keys are considered:
* ARROW_LEFT, ARROW_UP, ARROW_RIGHT, ARROW_DOWN
*
* @param {jQuery.Event} oEvent The event object of the <code>keydown</code>, <code>keyup</code> or <code>keypress</code> events.
* @static
* @returns {boolean}
* @since 1.24.0
*/
function isArrowKey(oEvent) {
var iKeyCode = oEvent.which, // jQuery oEvent.which normalizes oEvent.keyCode and oEvent.charCode
bArrowKey = (iKeyCode >= 37 && iKeyCode <= 40); // ARROW_LEFT, ARROW_UP, ARROW_RIGHT, ARROW_DOWN
switch (oEvent.type) {
case "keydown":
case "keyup":
return bArrowKey;
// note: the keypress event should be fired only when a character key is pressed,
// unfortunately some browsers fire the keypress event for other keys. e.g.:
//
// Firefox fire it for:
// ARROW_LEFT, ARROW_RIGHT
case "keypress":
// in Firefox, almost all noncharacter keys that fire the keypress event have a key code of 0
return iKeyCode === 0;
default:
return false;
}
}
/**
* Constructor for a jQuery.Event object.<br/>
* @see "http://www.jquery.com" and "http://api.jquery.com/category/events/event-object/".
*
* @class Check the jQuery.Event class documentation available under "http://www.jquery.com"<br/>
* and "http://api.jquery.com/category/events/event-object/" for details.
*
* @name jQuery.Event
* @public
*/
/**
* Returns OffsetX of Event. In jQuery there is a bug. In IE the value is in offsetX, in FF in layerX
*
* @returns {int} offsetX
* @public
*/
jQuery.Event.prototype.getOffsetX = function() {
if (this.type == 'click') {
if (this.offsetX) {
return this.offsetX;
}
if (this.layerX) {
return this.layerX;
}
if (this.originalEvent.layerX) {
return this.originalEvent.layerX;
}
}
// nothing defined -> offset = 0
return 0;
};
/**
* Returns OffsetY of Event. In jQuery there is a bug. in IE the value is in offsetY, in FF in layerY.
*
* @returns {int} offsetY
* @public
*/
jQuery.Event.prototype.getOffsetY = function() {
if (this.type == 'click') {
if (this.offsetY) {
return this.offsetY;
}
if (this.layerY) {
return this.layerY;
}
if (this.originalEvent.layerY) {
return this.originalEvent.layerY;
}
}
// nothing defined -> offset = 0
return 0;
};
// we still call the original stopImmediatePropagation
var fnStopImmediatePropagation = jQuery.Event.prototype.stopImmediatePropagation;
/**
* PRIVATE EXTENSION: allows to immediately stop the propagation of events in
* the event handler execution - means that "before" delegates can stop the
* propagation of the event to other delegates or the element and so on.
*
* @see sap.ui.core.Element.prototype._callEventHandles
* @param {boolean} bStopDelegate
*/
jQuery.Event.prototype.stopImmediatePropagation = function(bStopHandlers) {
// execute the original function
fnStopImmediatePropagation.apply(this, arguments);
// only set the stop handlers flag if it is wished...
if (bStopHandlers) {
this._bIsStopHandlers = true;
}
};
/**
* PRIVATE EXTENSION: check if the handler propagation has been stopped.
*
* @see sap.ui.core.Element.prototype._callEventHandles
*/
jQuery.Event.prototype.isImmediateHandlerPropagationStopped = function() {
return !!this._bIsStopHandlers;
};
/**
* Mark the event object for components that needs to know if the event was handled by a child component.
* PRIVATE EXTENSION
*
* @param {string} [sKey="handledByControl"]
* @param {string} [vValue=true]
*/
jQuery.Event.prototype.setMark = function(sKey, vValue) {
sKey = sKey || "handledByControl";
vValue = arguments.length < 2 ? true : vValue;
(this.originalEvent || this)["_sapui_" + sKey] = vValue;
};
/**
* Mark the event object for components that needs to know if the event was handled by a child component.
* PRIVATE EXTENSION
*
* @see jQuery.Event.prototype.setMark
* @param {string} [sKey="handledByControl"]
*/
jQuery.Event.prototype.setMarked = jQuery.Event.prototype.setMark;
/**
* Check whether the event object is marked by the child component or not.
* PRIVATE EXTENSION
*
* @param {string} [sKey="handledByControl"]
* @returns {boolean}
*/
jQuery.Event.prototype.isMarked = function(sKey) {
sKey = sKey || "handledByControl";
return !!(this.originalEvent || this)["_sapui_" + sKey];
};
/* ************** F6 Fast Navigation ************** */
// CustomData attribute name for fast navigation groups (in DOM additional prefix "data-" is needed)
jQuery.sap._FASTNAVIGATIONKEY = "sap-ui-fastnavgroup";
// Returns the nearest parent DomRef of the given DomRef with attribute data-sap-ui-customfastnavgroup="true".
function findClosestCustomGroup(oRef) {
var $Group = jQuery(oRef).closest('[data-sap-ui-customfastnavgroup="true"]');
return $Group[0];
}
// Returns the nearest parent DomRef of the given DomRef with attribute data-sap-ui-fastnavgroup="true" or
// (if available) the nearest parent with attribute data-sap-ui-customfastnavgroup="true".
function findClosestGroup(oRef) {
var oGroup = findClosestCustomGroup(oRef);
if (oGroup) {
return oGroup;
}
var $Group = jQuery(oRef).closest('[data-' + jQuery.sap._FASTNAVIGATIONKEY + '="true"]');
return $Group[0];
}
// Returns a jQuery object which contains all next/previous (bNext) tabbable DOM elements of the given starting point (oRef) within the given scopes (DOMRefs)
function findTabbables(oRef, aScopes, bNext) {
var $Ref = jQuery(oRef),
$All, $Tabbables;
if (bNext) {
$All = jQuery.merge($Ref.find("*"), jQuery.merge($Ref.nextAll(), $Ref.parents().nextAll()));
$Tabbables = $All.find(':sapTabbable').addBack(':sapTabbable');
} else {
$All = jQuery.merge($Ref.prevAll(), $Ref.parents().prevAll());
$Tabbables = jQuery.merge($Ref.parents(':sapTabbable'), $All.find(':sapTabbable').addBack(':sapTabbable'));
}
var $Tabbables = jQuery.unique($Tabbables);
return $Tabbables.filter(function(){
return isContained(aScopes, this);
});
}
// Filters all elements in the given jQuery object which are in the static UIArea and which are not in the given scopes.
function filterStaticAreaContent($Refs, aScopes){
var oStaticArea = jQuery.sap.domById("sap-ui-static");
if (!oStaticArea) {
return $Refs;
}
var aScopesInStaticArea = [];
for (var i = 0; i < aScopes.length; i++) {
if (jQuery.contains(oStaticArea, aScopes[i])) {
aScopesInStaticArea.push(aScopes[i]);
}
}
return $Refs.filter(function(){
if (aScopesInStaticArea.length && isContained(aScopesInStaticArea, this)) {
return true;
}
return !jQuery.contains(oStaticArea, this);
});
}
// Checks whether the given DomRef is contained or equals (in) one of the given container
function isContained(aContainers, oRef) {
for (var i = 0; i < aContainers.length; i++) {
if (aContainers[i] === oRef || jQuery.contains(aContainers[i], oRef)) {
return true;
}
}
return false;
}
//see navigate() (bForward = false)
function findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $Tabbables, oSouceGroup, bFindPreviousGroup) {
var oGroup, $Target;
for (var i = $Tabbables.length - 1; i >= 0; i--) {
oGroup = findClosestGroup($Tabbables[i]);
if (oGroup != oSouceGroup) {
if (bFindPreviousGroup) {
//First find last tabbable of previous group and remember this new group (named "X" in the following comments)
oSouceGroup = oGroup;
bFindPreviousGroup = false;
} else {
//Then starting from group X and try to find again the last tabbable of previous group (named "Y")
//-> Jump one tabbable back to get the first tabbable of X
$Target = jQuery($Tabbables[i + 1]);
break;
}
}
}
if (!$Target && !bFindPreviousGroup) {
//Group X found but not group Y -> X is the first group -> Focus the first tabbable scope (e.g. page) element
$Target = $FirstTabbableInScope;
}
return $Target;
}
// Finds the next/previous (bForward) element in the F6 chain starting from the given source element within the given scopes and focus it
function navigate(oSource, aScopes, bForward) {
if (!aScopes || aScopes.length == 0) {
aScopes = [document];
}
if (!isContained(aScopes, oSource)) {
return;
}
var oSouceGroup = findClosestGroup(oSource),
$AllTabbables = filterStaticAreaContent(jQuery(aScopes).find(':sapTabbable').addBack(':sapTabbable'), aScopes),
$FirstTabbableInScope = $AllTabbables.first(),
$Tabbables = filterStaticAreaContent(findTabbables(oSource, aScopes, bForward), aScopes),
oGroup, $Target;
if (bForward) {
//Find the first next tabbable within another group
for (var i = 0; i < $Tabbables.length; i++) {
oGroup = findClosestGroup($Tabbables[i]);
if (oGroup != oSouceGroup) {
$Target = jQuery($Tabbables[i]);
break;
}
}
//If not found, end of scope (e.g. page) is reached -> Focus the first tabbable scope (e.g. page) element
if (!$Target || !$Target.length) {
$Target = $FirstTabbableInScope;
}
} else {
$Target = findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $Tabbables, oSouceGroup, true);
if (!$Target || !$Target.length) {
//No other group found before -> find first element of last group in the scope (e.g. page)
if ($AllTabbables.length == 1) {
//Only one tabbable element -> use it
$Target = jQuery($AllTabbables[0]);
} else if ($AllTabbables.length > 1) {
oSouceGroup = findClosestGroup($AllTabbables.eq(-1));
oGroup = findClosestGroup($AllTabbables.eq(-2));
if (oSouceGroup != oGroup) {
//Last tabbable scope (e.g. page) element and the previous tabbable scope (e.g. page) element have different groups -> last tabbable scope (e.g. page) element is first tabbable element of its group
$Target = $AllTabbables.eq(-1);
} else {
//Take last tabbable scope (e.g. page) element as reference and start search for first tabbable of the same group
$Target = findFirstTabbableOfPreviousGroup($FirstTabbableInScope, $AllTabbables, oSouceGroup, false);
}
}
}
}
if ($Target && $Target.length) {
var oTarget = $Target[0],
oEvent = null,
oCustomGroup = findClosestCustomGroup(oTarget);
if (oCustomGroup && oCustomGroup.id) {
var oControl = sap.ui.getCore().byId(oCustomGroup.id);
if (oControl) {
oEvent = jQuery.Event("BeforeFastNavigationFocus");
oEvent.target = oTarget;
oEvent.source = oSource;
oEvent.forward = bForward;
oControl._handleEvent(oEvent);
}
}
if (!oEvent || !oEvent.isDefaultPrevented()) {
jQuery.sap.focus(oTarget);
}
}
}
/**
* Central handler for F6 key event. Based on the current target and the given event the next element in the F6 chain is focused.
*
* This handler might be also called manually. In this case the central handler is deactivated for the given event.
*
* If the event is not a keydown event, it does not represent the F6 key, the default behavior is prevented,
* the handling is explicitly skipped (<code>oSettings.skip</code>) or the target (<code>oSettings.target</code>) is not contained
* in the used scopes (<code>oSettings.scope</code>), the event is skipped.
*
* @param {jQuery.Event} oEvent a <code>keydown</code> event object.
* @param {object} [oSettings] further options in case the handler is called manually.
* @param {boolean} [oSettings.skip=false] whether the event should be ignored by the central handler (see above)
* @param {Element} [oSettings.target=document.activeElement] the DOMNode which should be used as starting point to find the next DOMNode in the F6 chain.
* @param {Element[]} [oSettings.scope=[document]] the DOMNodes(s) which are used for the F6 chain search
* @static
* @private
* @since 1.25.0
*/
jQuery.sap.handleF6GroupNavigation = function(oEvent, oSettings) {
if (oEvent.type != "keydown"
|| oEvent.keyCode != jQuery.sap.KeyCodes.F6
|| oEvent.isMarked("sapui5_handledF6GroupNavigation")
|| oEvent.isMarked()
|| oEvent.isDefaultPrevented()) {
return;
}
oEvent.setMark("sapui5_handledF6GroupNavigation");
oEvent.setMarked();
oEvent.preventDefault();
if (oSettings && oSettings.skip) {
return;
}
var oTarget = oSettings && oSettings.target ? oSettings.target : document.activeElement,
aScopes = null;
if (oSettings && oSettings.scope) {
aScopes = jQuery.isArray(oSettings.scope) ? oSettings.scope : [oSettings.scope];
}
navigate(oTarget, aScopes, !oEvent.shiftKey);
};
jQuery(function() {
jQuery(document).on("keydown", function(oEvent) {
jQuery.sap.handleF6GroupNavigation(oEvent, null);
});
});
/* ************************************************ */
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.keycodes.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*
* Provides constants for key codes. Useful in the implementation of keypress/keydown event handlers.
*/
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Enumeration of key codes.
*
* @namespace
* @public
* @since 0.9.0
*/
jQuery.sap.KeyCodes = {
/**
* @type number
* @public
*/
BACKSPACE : 8,
/**
* @type number
* @public
*/
TAB : 9,
/**
* @type number
* @public
*/
ENTER : 13,
/**
* @type number
* @public
*/
SHIFT : 16,
/**
* @type number
* @public
*/
CONTROL : 17,
/**
* @type number
* @public
*/
ALT : 18,
/**
* @type number
* @public
*/
BREAK : 19,
/**
* @type number
* @public
*/
CAPS_LOCK : 20,
/**
* @type number
* @public
*/
ESCAPE : 27,
/**
* @type number
* @public
*/
SPACE : 32,
/**
* @type number
* @public
*/
PAGE_UP : 33,
/**
* @type number
* @public
*/
PAGE_DOWN : 34,
/**
* @type number
* @public
*/
END : 35,
/**
* @type number
* @public
*/
HOME : 36,
/**
* @type number
* @public
*/
ARROW_LEFT : 37,
/**
* @type number
* @public
*/
ARROW_UP : 38,
/**
* @type number
* @public
*/
ARROW_RIGHT : 39,
/**
* @type number
* @public
*/
ARROW_DOWN : 40,
/**
* @type number
* @public
*/
PRINT : 44,
/**
* @type number
* @public
*/
INSERT : 45,
/**
* @type number
* @public
*/
DELETE : 46,
/**
* @type number
* @public
*/
DIGIT_0 : 48,
/**
* @type number
* @public
*/
DIGIT_1 : 49,
/**
* @type number
* @public
*/
DIGIT_2 : 50,
/**
* @type number
* @public
*/
DIGIT_3 : 51,
/**
* @type number
* @public
*/
DIGIT_4 : 52,
/**
* @type number
* @public
*/
DIGIT_5 : 53,
/**
* @type number
* @public
*/
DIGIT_6 : 54,
/**
* @type number
* @public
*/
DIGIT_7 : 55,
/**
* @type number
* @public
*/
DIGIT_8 : 56,
/**
* @type number
* @public
*/
DIGIT_9 : 57,
/**
* @type number
* @public
*/
A : 65,
/**
* @type number
* @public
*/
B : 66,
/**
* @type number
* @public
*/
C : 67,
/**
* @type number
* @public
*/
D : 68,
/**
* @type number
* @public
*/
E : 69,
/**
* @type number
* @public
*/
F : 70,
/**
* @type number
* @public
*/
G : 71,
/**
* @type number
* @public
*/
H : 72,
/**
* @type number
* @public
*/
I : 73,
/**
* @type number
* @public
*/
J : 74,
/**
* @type number
* @public
*/
K : 75,
/**
* @type number
* @public
*/
L : 76,
/**
* @type number
* @public
*/
M : 77,
/**
* @type number
* @public
*/
N : 78,
/**
* @type number
* @public
*/
O : 79,
/**
* @type number
* @public
*/
P : 80,
/**
* @type number
* @public
*/
Q : 81,
/**
* @type number
* @public
*/
R : 82,
/**
* @type number
* @public
*/
S : 83,
/**
* @type number
* @public
*/
T : 84,
/**
* @type number
* @public
*/
U : 85,
/**
* @type number
* @public
*/
V : 86,
/**
* @type number
* @public
*/
W : 87,
/**
* @type number
* @public
*/
X : 88,
/**
* @type number
* @public
*/
Y : 89,
/**
* @type number
* @public
*/
Z : 90,
/**
* @type number
* @public
*/
WINDOWS : 91,
/**
* @type number
* @public
*/
CONTEXT_MENU : 93,
/**
* @type number
* @public
*/
TURN_OFF : 94,
/**
* @type number
* @public
*/
SLEEP : 95,
/**
* @type number
* @public
*/
NUMPAD_0 : 96,
/**
* @type number
* @public
*/
NUMPAD_1 : 97,
/**
* @type number
* @public
*/
NUMPAD_2 : 98,
/**
* @type number
* @public
*/
NUMPAD_3 : 99,
/**
* @type number
* @public
*/
NUMPAD_4 : 100,
/**
* @type number
* @public
*/
NUMPAD_5 : 101,
/**
* @type number
* @public
*/
NUMPAD_6 : 102,
/**
* @type number
* @public
*/
NUMPAD_7 : 103,
/**
* @type number
* @public
*/
NUMPAD_8 : 104,
/**
* @type number
* @public
*/
NUMPAD_9 : 105,
/**
* @type number
* @public
*/
NUMPAD_ASTERISK : 106,
/**
* @type number
* @public
*/
NUMPAD_PLUS : 107,
/**
* @type number
* @public
*/
NUMPAD_MINUS : 109,
/**
* @type number
* @public
*/
NUMPAD_COMMA : 110,
/**
* @type number
* @public
*/
NUMPAD_SLASH : 111,
/**
* @type number
* @public
*/
F1 : 112,
/**
* @type number
* @public
*/
F2 : 113,
/**
* @type number
* @public
*/
F3 : 114,
/**
* @type number
* @public
*/
F4 : 115,
/**
* @type number
* @public
*/
F5 : 116,
/**
* @type number
* @public
*/
F6 : 117,
/**
* @type number
* @public
*/
F7 : 118,
/**
* @type number
* @public
*/
F8 : 119,
/**
* @type number
* @public
*/
F9 : 120,
/**
* @type number
* @public
*/
F10 : 121,
/**
* @type number
* @public
*/
F11 : 122,
/**
* @type number
* @public
*/
F12 : 123,
/**
* @type number
* @public
*/
NUM_LOCK : 144,
/**
* @type number
* @public
*/
SCROLL_LOCK : 145,
/**
* @type number
* @public
*/
OPEN_BRACKET : 186,
/**
* @type number
* @public
*/
PLUS : 187,
/**
* @type number
* @public
*/
COMMA : 188,
/**
* @type number
* @public
*/
SLASH : 189,
/**
* @type number
* @public
*/
DOT : 190,
/**
* @type number
* @public
*/
PIPE : 191,
/**
* @type number
* @public
*/
SEMICOLON : 192,
/**
* @type number
* @public
*/
MINUS : 219,
/**
* @type number
* @public
*/
GREAT_ACCENT : 220,
/**
* @type number
* @public
*/
EQUALS : 221,
/**
* @type number
* @public
*/
SINGLE_QUOTE : 222,
/**
* @type number
* @public
*/
BACKSLASH : 226
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.mobile.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
//Provides common helper functions for the mobile version of UI5
sap.ui.define(['jquery.sap.global', 'sap/ui/Device', 'jquery.sap.dom', 'jquery.sap.events'],
function(jQuery, Device/* , jQuerySap1, jQuerySap2 */) {
"use strict";
(function($) { // TODO get rid of inner scope function, rename $ to jQuery
var FAKE_OS_PATTERN = /(?:\?|&)sap-ui-xx-fakeOS=([^&]+)/;
$.sap.simulateMobileOnDesktop = false;
// OS overriding mechanism
if ((Device.browser.webkit || (Device.browser.msie && Device.browser.version >= 10)) && !jQuery.support.touch) { // on non-touch webkit browsers and IE10 we are interested in overriding
var result = document.location.search.match(FAKE_OS_PATTERN);
var resultUA = result && result[1] || jQuery.sap.byId("sap-ui-bootstrap").attr("data-sap-ui-xx-fakeOS");
if (resultUA) {
$.sap.simulateMobileOnDesktop = true;
var ua = { // for "ios"/"android"/"blackberry" we have defined fake user-agents; these will affect all other browser/platform detection mechanisms
ios: "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.48 (KHTML, like Gecko) Version/5.1 Mobile/9A406 Safari/7534.48.3",
iphone: "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.48 (KHTML, like Gecko) Version/5.1 Mobile/9A406 Safari/7534.48.3",
ipad: "Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206",
android: "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9100 Build/IML74K) AppleWebKit/534.46 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.46",
android_phone: "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9100 Build/IML74K) AppleWebKit/534.46 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.46",
android_tablet: "Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19",
blackberry: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
winphone: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)"
}[resultUA];
if (ua &&
(Device.browser.webkit && resultUA !== "winphone" || Device.browser.msie && resultUA === "winphone")) { // only for the working combinations
// code for modifying the real user-agent
if (Device.browser.safari) {
var __originalNavigator = window.navigator;
window.navigator = {};
/*eslint-disable no-proto */
window.navigator.__proto__ = __originalNavigator;
/*eslint-enable no-proto */
window.navigator.__defineGetter__('userAgent', function(){ return ua; });
} else { // Chrome, IE10
Object.defineProperty(navigator, "userAgent", {
get: function() {
return ua;
}
});
}
if (Device.browser.webkit) {
// all downstream checks will be fine with the faked user-agent.
// But now we also need to adjust the wrong upstream settings in jQuery:
jQuery.browser.msie = jQuery.browser.opera = jQuery.browser.mozilla = false;
jQuery.browser.webkit = true;
jQuery.browser.version = "534.46"; // this is not exactly true for all UAs, but there are much bigger shortcomings of this approach than a minor version of the browser, so giving the exact value is not worth the effort
} // else in IE10 with winphone emulation, jQuery.browser has already the correct information
// update the sap.ui.Device.browser.* information
Device._update($.sap.simulateMobileOnDesktop);
}
}
}
/**
* Holds information about the current operating system
*
* @name jQuery.os
* @namespace
* @deprecated since 1.20: use sap.ui.Device.os
* @public
*/
$.os = $.extend(/** @lends jQuery.os */ {
/**
* The name of the operating system; currently supported are: "ios", "android", "blackberry"
* @type {string}
* @deprecated since 1.20: use sap.ui.Device.os.name
* @public
*/
os: Device.os.name,
/**
* The version of the operating system as a string (including minor versions)
* @type {string}
* @deprecated since 1.20: use sap.ui.Device.os.versionStr
* @public
*/
version: Device.os.versionStr,
/**
* The version of the operating system parsed as a float (major and first minor version)
* @type {float}
* @deprecated since 1.20: use sap.ui.Device.os.version
* @public
*/
fVersion: Device.os.version
}, $.os);
$.os[Device.os.name] = true;
/**
* Whether the current operating system is Android
* @type {boolean}
* @public
* @deprecated since 1.20: use sap.ui.Device.os.android
* @name jQuery.os.android
*/
/**
* Whether the current operating system is BlackBerry
* @type {boolean}
* @public
* @deprecated since 1.20: use sap.ui.Device.os.blackberry
* @name jQuery.os.blackberry
*/
/**
* Whether the current operating system is Apple iOS
* @type {boolean}
* @public
* @deprecated since 1.20: use sap.ui.Device.os.ios
* @name jQuery.os.ios
*/
/**
* Whether the current operating system is Windows Phone
* @type {boolean}
* @public
* @deprecated since 1.20: use sap.ui.Device.os.winphone
* @name jQuery.os.winphone
*/
// feature and state detection
$.extend( $.support, {
/**
* Whether the device has a retina display (window.devicePixelRatio >= 2)
* @type {boolean}
* @public
*/
retina: window.devicePixelRatio >= 2
});
/**
* @name jQuery.device
* @namespace
* @deprecated since 1.20: use the respective functions of sap.ui.Device
* @public
*/
$.device = $.extend({}, $.device);
/**
* Holds information about the current device and its state
*
* @name jQuery.device.is
* @namespace
* @deprecated since 1.20: use the respective functions of sap.ui.Device
* @public
*/
$.device.is = $.extend( /** @lends jQuery.device.is */ {
/**
* Whether the application runs in standalone mode without browser UI (launched from the iOS home screen)
* @type {boolean}
* @deprecated since 1.20: use window.navigator.standalone
* @public
*/
standalone: window.navigator.standalone,
/**
* Whether the device is in "landscape" orientation (also "true" when the device does not know about the orientation)
* @type {boolean}
* @deprecated since 1.20: use sap.ui.Device.orientation.landscape
* @public
*/
landscape: Device.orientation.landscape,
/**
* Whether the device is in portrait orientation
* @type {boolean}
* @deprecated since 1.20: use sap.ui.Device.orientation.portrait
* @public
*/
portrait: Device.orientation.portrait,
/**
* Whether the application runs on an iPhone
* @type {boolean}
* @deprecated since 1.20: shouldn't do device specific coding; if still needed, use sap.ui.Device.os.ios &amp;&amp; sap.ui.Device.system.phone
* @public
*/
iphone: Device.os.ios && Device.system.phone,
/**
* Whether the application runs on an iPad
* @type {boolean}
* @deprecated since 1.20: shouldn't do device specific coding; if still needed, use sap.ui.Device.os.ios &amp;&amp; sap.ui.Device.system.tablet
* @public
*/
ipad: Device.os.ios && Device.system.tablet,
/**
* Whether the application runs on an Android phone - based not on screen size but user-agent (so this is not guaranteed to be equal to jQuery.device.is.phone on Android)
* https://developers.google.com/chrome/mobile/docs/user-agent
* Some device vendors however do not follow this rule
* @deprecated since 1.17.0: use sap.ui.Device.system.phone &amp;&amp; sap.ui.Device.os.android instead
* @type {boolean}
* @public
*/
android_phone: Device.system.phone && Device.os.android,
/**
* Whether the application runs on an Android tablet - based not on screen size but user-agent (so this is not guaranteed to be equal to jQuery.device.is.tablet on Android)
* https://developers.google.com/chrome/mobile/docs/user-agent
* Some device vendors however do not follow this rule
* @type {boolean}
* @deprecated since 1.17.0: use sap.ui.Device.system.tablet &amp;&amp; sap.ui.Device.os.android instead
* @public
*/
android_tablet: Device.system.tablet && Device.os.android,
/**
* Whether the running device is a tablet.
* If a desktop browser runs in mobile device simulation mode (with URL parameter sap-ui-xx-fakeOS or sap-ui-xx-test-mobile),
* this property will also be set according to the simulated platform.
* This property will be false when runs in desktop browser.
* @type {boolean}
* @deprecated since 1.17.0: use sap.ui.Device.system.tablet instead
* @public
*/
tablet: Device.system.tablet,
/**
* Whether the running device is a phone.
* If a desktop browser runs in mobile device simulation mode (with URL parameter sap-ui-xx-fakeOS or sap-ui-xx-test-mobile),
* this property will also be set according to the simulated platform.
* This property will be false when runs in desktop browser.
* @type {boolean}
* @deprecated since 1.17.0: use sap.ui.Device.system.phone instead
* @public
*/
phone: Device.system.phone,
/**
* Whether the running device is a desktop browser.
* If a desktop browser runs in mobile device simulation mode (with URL parameter sap-ui-xx-fakeOS or sap-ui-xx-test-mobile),
* this property will be false.
* @type {boolean}
* @deprecated since 1.17.0: use sap.ui.Device.system.desktop instead
* @public
*/
desktop: Device.system.desktop
},$.device.is);
// Windows Phone specific handling
if (sap.ui.Device.os.windows_phone) {
// Disable grey highlights over tapped areas.
// This meta tag works since Windows 8.1.
// Write in-place, otherwise IE ignores it:
document.write('<meta name="msapplication-tap-highlight" content="no">');
// Style for correct viewport size and scale definition.
// It works correctly since Windows 8.1.
// Older 8.0 patches return wrong device-width:
document.write('<style>@-ms-viewport{width:device-width;}</style>');
}
var _bInitMobileTriggered = false;
/**
* Does some basic modifications to the HTML page that make it more suitable for mobile apps.
* Only the first call to this method is executed, subsequent calls are ignored. Note that this method is also called by the constructor of toplevel controls like sap.m.App, sap.m.SplitApp and sap.m.Shell.
* Exception: if no homeIcon was set, subsequent calls have the chance to set it.
*
* The "options" parameter configures what exactly should be done.
*
* It can have the following properties:
* <ul>
* <li>viewport: whether to set the viewport in a way that disables zooming (default: true)</li>
* <li>statusBar: the iOS status bar color, "default", "black" or "black-translucent" (default: "default")</li>
* <li>hideBrowser: whether the browser UI should be hidden as far as possible to make the app feel more native (default: true)</li>
* <li>preventScroll: whether native scrolling should be disabled in order to prevent the "rubber-band" effect where the whole window is moved (default: true)</li>
* <li>preventPhoneNumberDetection: whether Safari Mobile should be prevented from transforming any numbers that look like phone numbers into clickable links; this should be left as "true", otherwise it might break controls because Safari actually changes the DOM. This only affects all page content which is created after initMobile is called.</li>
* <li>rootId: the ID of the root element that should be made fullscreen; only used when hideBrowser is set (default: the document.body)</li>
* <li>useFullScreenHeight: a boolean that defines whether the height of the html root element should be set to 100%, which is required for other elements to cover the full height (default: true)</li>
* <li>homeIcon: deprecated since 1.12, use jQuery.sap.setIcons instead.
* </ul>
*
* @param {object} [options] configures what exactly should be done
* @param {boolean} [options.viewport=true] whether to set the viewport in a way that disables zooming
* @param {string} [options.statusBar='default'] the iOS status bar color, "default", "black" or "black-translucent"
* @param {boolean} [options.hideBrowser=true] whether the browser UI should be hidden as far as possible to make the app feel more native
* @param {boolean} [options.preventScroll=true] whether native scrolling should be disabled in order to prevent the "rubber-band" effect where the whole window is moved
* @param {boolean} [options.preventPhoneNumberDetection=true] whether Safari mobile should be prevented from transforming any numbers that look like phone numbers into clickable links
* @param {string} [options.rootId] the ID of the root element that should be made fullscreen; only used when hideBrowser is set. If not set, the body is used
* @param {boolean} [options.useFullScreenHeight=true] whether the height of the html root element should be set to 100%, which is required for other elements to cover the full height
* @param {string} [options.homeIcon=undefined] deprecated since 1.12, use jQuery.sap.setIcons instead.
* @param {boolean} [options.homeIconPrecomposed=false] deprecated since 1.12, use jQuery.sap.setIcons instead.
* @param {boolean} [options.mobileWebAppCapable=true] whether the Application will be loaded in full screen mode after added to home screen on mobile devices. The default value for this property only enables the full screen mode when runs on iOS device.
*
* @name jQuery.sap.initMobile
* @function
* @public
*/
$.sap.initMobile = function(options) {
var $head = $("head");
if (!_bInitMobileTriggered) { // only one initialization per HTML page
_bInitMobileTriggered = true;
options = $.extend({}, { // merge in the default values
viewport: true,
statusBar: "default",
hideBrowser: true,
preventScroll: true,
preventPhoneNumberDetection: true,
useFullScreenHeight: true,
homeIconPrecomposed: false,
mobileWebAppCapable: "default"
}, options);
// en-/disable automatic link generation for phone numbers
if (Device.os.ios && options.preventPhoneNumberDetection) {
$head.append($('<meta name="format-detection" content="telephone=no">')); // this only works for all DOM created afterwards
} else if (Device.browser.msie) {
$head.append($('<meta http-equiv="cleartype" content="on">'));
$head.append($('<meta name="msapplication-tap-highlight" content="no">'));
}
var bIsIOS7Safari = Device.os.ios && Device.os.version >= 7 && Device.os.version < 8 && Device.browser.name === "sf";
// initialize viewport
if (options.viewport) {
var sMeta;
if (bIsIOS7Safari && Device.system.phone) {
//if the softkeyboard is open in orientation change, we have to do this to solve the zoom bug on the phone -
//the phone zooms into the view although it shouldn't so these two lines will zoom out again see orientation change below
//the important part seems to be removing the device width
sMeta = 'minimal-ui, initial-scale=1.0, maximum-scale=1.0, user-scalable=0';
} else if (bIsIOS7Safari && Device.system.tablet) {
//remove the width = device width since it will not work correctly if the webside is embedded in a webview
sMeta = 'initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
} else if ($.device.is.iphone && (Math.max(window.screen.height, window.screen.width) === 568)) {
// iPhone 5
sMeta = "user-scalable=0, initial-scale=1.0";
} else if (Device.os.android && Device.os.version < 3) {
sMeta = "width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
} else {
// all other devices
sMeta = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
}
$head.append($('<meta name="viewport" content="' + sMeta + '">'));
}
if (options.mobileWebAppCapable === "default") {
if (Device.os.ios) {
// keep the old behavior for compatibility
// enable fullscreen mode only when runs on iOS devices
$head.append($('<meta name="apple-mobile-web-app-capable" content="yes">')); // since iOS 2.1
}
} else {
$.sap.setMobileWebAppCapable(options.mobileWebAppCapable);
}
if (Device.os.ios) {
// set the status bar style on Apple devices
$head.append($('<meta name="apple-mobile-web-app-status-bar-style" content="' + options.statusBar + '">')); // "default" or "black" or "black-translucent", since iOS 2.1
// splash screen
//<link rel="apple-touch-startup-image" href="/startup.png">
}
if (options.preventScroll) {
$(window).bind("touchmove", function sapInitMobileTouchMoveHandle(oEvent) {
if (!oEvent.isMarked()) {
oEvent.preventDefault(); // prevent the rubber-band effect
}
});
}
if (options.useFullScreenHeight) {
$(function() {
document.documentElement.style.height = "100%"; // set html root tag to 100% height
});
}
}
if (options && options.homeIcon) {
var oIcons;
if (typeof options.homeIcon === "string") {
oIcons = { phone: options.homeIcon };
} else {
oIcons = $.extend({}, options.homeIcon);
}
oIcons.precomposed = options.homeIconPrecomposed || oIcons.precomposed;
oIcons.favicon = options.homeIcon.icon || oIcons.favicon;
oIcons.icon = undefined;
$.sap.setIcons(oIcons);
}
};
/**
* Sets the bookmark icon for desktop browsers and the icon to be displayed on the home screen of iOS devices after the user does "add to home screen".
*
* Only call this method once and call it early when the page is loading: browsers behave differently when the favicon is modified while the page is alive.
* Some update the displayed icon inside the browser but use an old icon for bookmarks.
* When a favicon is given, any other existing favicon in the document will be removed.
* When at least one home icon is given, all existing home icons will be removed and new home icon tags for all four resolutions will be created.
*
* The home icons must be in PNG format and given in different sizes for iPad/iPhone with and without retina display.
* The favicon is used in the browser and for desktop shortcuts and should optimally be in ICO format:
* PNG does not seem to be supported by Internet Explorer and ICO files can contain different image sizes for different usage locations. E.g. a 16x16px version
* is used inside browsers.
*
* All icons are given in an an object holding icon URLs and other settings. The properties of this object are:
* <ul>
* <li>phone: a 57x57 pixel version for non-retina iPhones</li>
* <li>tablet: a 72x72 pixel version for non-retina iPads</li>
* <li>phone@2: a 114x114 pixel version for retina iPhones</li>
* <li>tablet@2: a 144x144 pixel version for retina iPads</li>
* <li>precomposed: whether the home icons already have some glare effect (otherwise iOS will add it) (default: false)</li>
* <li>favicon: the ICO file to be used inside the browser and for desktop shortcuts</li>
* </ul>
*
* One example is:
* <pre>
* {
* 'phone':'phone-icon_57x57.png',
* 'phone@2':'phone-retina_117x117.png',
* 'tablet':'tablet-icon_72x72.png',
* 'tablet@2':'tablet-retina_144x144.png',
* 'precomposed':true,
* 'favicon':'desktop.ico'
* }
* </pre>
* If one of the sizes is not given, the largest available alternative image will be used instead for this size.
* On Android these icons may or may not be used by the device. Apparently chances can be improved by using icons with glare effect, so the "precomposed" property can be set to "true". Some Android devices may also use the favicon for bookmarks instead of the home icons.</li>
*
* @param {object} oIcons
* @name jQuery.sap.setIcons
* @function
* @public
*/
$.sap.setIcons = function(oIcons) {
if (!oIcons || (typeof oIcons !== "object")) {
$.sap.log.warning("Call to jQuery.sap.setIcons() has been ignored because there were no icons given or the argument was not an object.");
return;
}
var $head = $("head"),
precomposed = oIcons.precomposed ? "-precomposed" : "",
getBestFallback = function(res) {
return oIcons[res] || oIcons['tablet@2'] || oIcons['phone@2'] || oIcons['phone'] || oIcons['tablet']; // fallback logic
},
mSizes = {
"phone": "",
"tablet": "72x72",
"phone@2": "114x114",
"tablet@2": "144x144"
};
// desktop icon
if (oIcons["favicon"]) {
// remove any other favicons
var $fav = $head.find("[rel^=shortcut]"); // cannot search for "shortcut icon"
$fav.each(function(){
if (this.rel === "shortcut icon") {
$(this).remove();
}
});
// create favicon
$head.append($('<link rel="shortcut icon" href="' + oIcons["favicon"] + '" />'));
}
// mobile home screen icons
if (getBestFallback("phone")) {
// if any home icon is given remove old ones
$head.find("[rel=apple-touch-icon]").remove();
$head.find("[rel=apple-touch-icon-precomposed]").remove();
}
for (var platform in mSizes) {
oIcons[platform] = oIcons[platform] || getBestFallback(platform);
if (oIcons[platform]) {
var size = mSizes[platform];
$head.append($('<link rel="apple-touch-icon' + precomposed + '" ' + (size ? 'sizes="' + size + '"' : "") + ' href="' + oIcons[platform] + '" />'));
}
}
};
/**
* Sets the "apple-mobile-web-app-capable" and "mobile-web-app-capable" meta information which defines whether the application is loaded
* in full screen mode (browser address bar and toolbar are hidden) after the user does "add to home screen" on mobile devices. Currently
* this meta tag is only supported by iOS Safari and mobile Chrome from version 31.
*
* If the application opens new tabs because of attachments, url and so on, setting this to false will let the user be able to go from the
* new tab back to the application tab after the application is added to home screen.
*
* Note: this function only has effect when the application runs on iOS Safari and mobile Chrome from version 31.
*
* @param {boolean} bValue whether the Application will be loaded in full screen mode after added to home screen from iOS Safari or mobile Chrome from version 31.
* @name jQuery.sap.setMobileWebAppCapable
* @function
* @public
*/
$.sap.setMobileWebAppCapable = function(bValue) {
if (!Device.system.tablet && !Device.system.phone) {
return;
}
var $Head = $("head"),
aPrefixes = ["", "apple"],
sNameBase = "mobile-web-app-capable",
sContent = bValue ? "yes" : "no",
i, sName, $WebAppMeta;
for (i = 0 ; i < aPrefixes.length ; i++) {
sName = aPrefixes[i] ? (aPrefixes[i] + "-" + sNameBase) : sNameBase;
$WebAppMeta = $Head.children('meta[name="' + sName + '"]');
if ($WebAppMeta.length) {
$WebAppMeta.attr("content", sContent);
} else {
$Head.append($('<meta name="' + sName + '" content="' + sContent + '">'));
}
}
};
})(jQuery);
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.properties.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides access to Java-like properties files
sap.ui.define(['jquery.sap.global', 'jquery.sap.sjax'],
function(jQuery/* , jQuerySap1 */) {
"use strict";
// Javadoc for private inner class "Properties" - this list of comments is intentional!
/**
* @interface Represents a list of properties (key/value pairs).
*
* Each key and its corresponding value in the property list is a string.
* Values are unicode escaped \ue0012.
* Keys are case-sensitive and only alpha-numeric characters with a leading character are allowed.
*
* Use {@link jQuery.sap.properties} to create an instance of jQuery.sap.util.Properties.
*
* The getProperty method is used to retrieve a value from the list.
* The setProperty method is used to store or change a property in the list.
* Additionally, the getKeys method can be used to retrieve an array of all keys that are
* currently in the list.
*
* @author SAP SE
* @version 1.28.5
* @since 0.9.0
* @name jQuery.sap.util.Properties
* @public
*/
/**
* Returns the value of a given key. Optionally, a given default value is returned if the requested key is not in the list.
* @param {string} sKey The key of the property
* @param {string} [sDefaultValue] Optional, the default value if the requested key is not in the list.
* @return {string} The value of a given key. The default value (if given) is returned if the requested key is not in the list.
*
* @function
* @name jQuery.sap.util.Properties.prototype.getProperty
*/
/**
* Returns an array of all keys in the property list.
* @return {array} All keys in the property list.
*
* @function
* @name jQuery.sap.util.Properties.prototype.getKeys
*/
/**
* Adds or changes a given key to/in the list.
* @param {string} sKey The key of the property
* @param {string} sValue The value for the key with unicode encoding.
*
* @function
* @name jQuery.sap.util.Properties.prototype.setProperty
*/
/**
* Creates and returns a clone of the property list.
* @return {jQuery.sap.util.Properties} A clone of the property list
*
* @function
* @name jQuery.sap.util.Properties.prototype.clone
*/
/*
* Implements jQuery.sap.util.Properties
*/
var Properties = function() {
this.mProperties = {};
this.aKeys = [];
};
/*
* Implements jQuery.sap.util.Properties.prototype.getProperty
*/
Properties.prototype.getProperty = function(sKey, sDefaultValue) {
var sValue = this.mProperties[sKey];
if (typeof (sValue) == "string") {
return sValue;
} else if (sDefaultValue) {
return sDefaultValue;
}
return null;
};
/*
* Implements jQuery.sap.util.Properties.prototype.getKeys
*/
Properties.prototype.getKeys = function() {
return this.aKeys;
};
/*
* Implements jQuery.sap.util.Properties.prototype.setProperty
*/
Properties.prototype.setProperty = function(sKey, sValue) {
if (typeof (sValue) != "string") {
return;
}
if (typeof (this.mProperties[sKey]) != "string") {
this.aKeys.push(sKey);
}
this.mProperties[sKey] = sValue;
};
/*
* Implements jQuery.sap.util.Properties.prototype.clone
*/
Properties.prototype.clone = function() {
var oClone = new Properties();
oClone.mProperties = jQuery.extend({}, this.mProperties);
oClone.aKeys = jQuery.merge([], this.aKeys);
return oClone;
};
/*
* Saves the property list to a given URL using a POST request.
*/
//sap.ui.resource.Properties.prototype.save = function(sUrl) {
// return jQuery.sap.syncPost(sUrl, this.mProperties);
//};
/**
* RegExp used to split file into lines, also removes leading whitespace.
* Note: group must be non-capturing, otherwise the line feeds will be part of the split result.
*/
var rLines = /(?:^|\r\n|\r|\n)[ \t\f]*/;
/**
* RegExp that handles escapes, continuation line markers and key/value separators
*
* [---unicode escape--] [esc] [cnt] [---key/value separator---]
*/
var rEscapes = /(\\u[0-9a-fA-F]{0,4})|(\\.)|(\\$)|([ \t\f]*[ \t\f:=][ \t\f]*)/g;
/**
* Special escape characters as supported by properties format
* @see JDK API doc for java.util.Properties
*/
var mEscapes = {
'\\f' : '\f',
'\\n' : '\n',
'\\r' : '\r',
'\\t' : '\t'
};
/*
* Parses the given text sText and sets the properties
* in the properties object oProp accordingly.
* @param {string} sText the text to parse
* @param oProp the properties object to fill
* @private
*/
function parse(sText, oProp) {
var aLines = sText.split(rLines), // split file into lines
sLine,sKey,sValue,bKey,i,m,iLastIndex;
oProp.mProperties = {};
oProp.aKeys = [];
for (i = 0; i < aLines.length; i++) {
sLine = aLines[i];
// ignore empty lines
if (sLine === "" || sLine.charAt(0) === "#" || sLine.charAt(0) === "!" ) {
continue;
}
rEscapes.lastIndex = iLastIndex = 0;
sValue = "";
bKey = true;
while ( (m = rEscapes.exec(sLine)) !== null ) {
// handle any raw, unmatched input
if ( iLastIndex < m.index ) {
sValue += sLine.slice(iLastIndex, m.index);
}
iLastIndex = rEscapes.lastIndex;
if ( m[1] ) {
// unicode escape
if ( m[1].length !== 6 ) {
throw new Error("Incomplete Unicode Escape '" + m[1] + "'");
}
sValue += String.fromCharCode(parseInt(m[1].slice(2), 16));
} else if ( m[2] ) {
// special or simple escape
sValue += mEscapes[m[2]] || m[2].slice(1);
} else if ( m[3] ) {
// continuation line marker
sLine = aLines[++i];
rEscapes.lastIndex = iLastIndex = 0;
} else if ( m[4] ) {
// key/value separator
if ( bKey ) {
bKey = false;
sKey = sValue;
sValue = "";
} else {
sValue += m[4];
}
}
}
if ( iLastIndex < sLine.length ) {
sValue += sLine.slice(iLastIndex);
}
if ( bKey ) {
sKey = sValue;
sValue = "";
}
oProp.aKeys.push(sKey);
oProp.mProperties[sKey] = sValue;
}
// remove duplicates from keyset (sideeffect:sort)
jQuery.sap.unique(oProp.aKeys);
}
/**
* Creates and returns a new instance of {@link jQuery.sap.util.Properties}.
*
* If option 'url' is passed, immediately a load request for the given target is triggered.
* A property file that is loaded can contain comments with a leading ! or #.
* The loaded property list does not contain any comments.
*
* <b>Example for loading a property file:</b>
* <pre>
* jQuery.sap.properties({url : "../myProperty.properties"});
* </pre>
*
* <b>Example for creating an empty properties instance:</b>
* <pre>
* jQuery.sap.properties();
* </pre>
*
* <b>Examples for getting and setting properties:</b>
* <pre>
* var oProperties = jQuery.sap.properties();
* oProperties.setProperty("KEY_1","Test Key");
* var sValue1 = oProperties.getProperty("KEY_1");
* var sValue2 = oProperties.getProperty("KEY_2","Default");
* </pre>
*
* @public
* @param {object} [mParams] Parameters used to initialize the property list
* @param {string} [mParams.url] The URL to the .properties file which should be loaded.
* @param {boolean} [mParams.async] Whether the .properties file which should be loaded asynchronously (Default: <code>false</code>)
* @param {object} [mParams.headers] A map of additional header key/value pairs to send along with the request (see headers option of jQuery.ajax).
* @return {jQuery.sap.util.Properties|Promise} A new property list instance (synchronous case). In case of asynchronous loading an ECMA Script 6 Promise is returned.
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.properties = function properties(mParams) {
mParams = jQuery.extend({url: undefined, headers: {}}, mParams);
var bAsync = !!mParams.async,
oProp = new Properties();
function _parse(sText){
if (typeof (sText) == "string") {
parse(sText, oProp);
}
}
function _load(){
var oRes;
if (typeof (mParams.url) == "string") {
oRes = jQuery.sap.loadResource({
url: mParams.url,
dataType: 'text',
headers: mParams.headers,
failOnError: false,
async: bAsync
});
}
return oRes;
}
if (bAsync) {
return new window.Promise(function(resolve, reject){
var oRes = _load();
if (!oRes) {
resolve(oProp);
return;
}
oRes.then(function(oVal){
try {
_parse(oVal);
resolve(oProp);
} catch (e) {
reject(e);
}
}, function(oVal){
reject(oVal instanceof Error ? oVal : new Error("Problem during loading of property file '" + mParams.url + "': " + oVal));
});
});
} else {
_parse(_load());
return oProp;
}
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.resources.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides access to Java-like resource bundles in properties file format
sap.ui.define(['jquery.sap.global', 'jquery.sap.properties', 'jquery.sap.strings'],
function(jQuery/* , jQuerySap1, jQuerySap2 */) {
"use strict";
/*global Promise*/
// Javadoc for private inner class "Bundle" - this list of comments is intentional!
/**
* @interface Contains locale-specific texts.
*
* If you need a locale-specific text within your application, you can use the
* resource bundle to load the locale-specific file from the server and access
* the texts of it.
*
* Use {@link jQuery.sap.resources} to create an instance of jQuery.sap.util.ResourceBundle.
* There you have to specify the URL to the base .properties file of a bundle
* (.properties without any locale information, e.g. "mybundle.properties"), and optionally
* a locale. The locale is defined as a string of the language and an optional country code
* separated by underscore (e.g. "en_GB" or "fr"). If no locale is passed, the default
* locale is "en" if the SAPUI5 framework is not available. Otherwise the default locale is taken from
* the SAPUI5 configuration.
*
* With the getText() method of the resource bundle, a locale-specific string value
* for a given key will be returned.
*
* With the given locale, the ResourceBundle requests the locale-specific properties file
* (e.g. "mybundle_fr_FR.properties"). If no file is found for the requested locale or if the file
* does not contain a text for the given key, a sequence of fall back locales is tried one by one.
* First, if the locale contains a region information (fr_FR), then the locale without the region is
* tried (fr). If that also can't be found or doesn't contain the requested text, the english file
* is used (en - assuming that most development projects contain at least english texts).
* If that also fails, the file without locale (base URL of the bundle) is tried.
*
* If none of the requested files can be found or none of them contains a text for the given key,
* then the key itself is returned as text.
*
* Exception: Fallback for "zh_HK" is "zh_TW" before zh.
*
* @author SAP SE
* @version 1.28.5
* @since 0.9.0
* @name jQuery.sap.util.ResourceBundle
* @public
*/
/**
* Returns a locale-specific string value for the given key sKey.
*
* The text is searched in this resource bundle according to the fallback chain described in
* {@link jQuery.sap.util.ResourceBundle}. If no text could be found, the key itself is used as text.
*
* If text parameters are given, then any occurrences of the pattern "{<i>n</i>}" with <i>n</i> being an integer
* are replaced by the parameter value with index <i>n</i>. Note: This replacement is also applied if no text had been found (key).
* For more details on this replacement mechanism refer also:
* @see jQuery.sap#formatMessage
*
* @param {string} sKey
* @param {string[]} [aArgs] List of parameters which should replace the place holders "{n}" (n is the index) in the found locale-specific string value.
* @return {string} The value belonging to the key, if found; otherwise the key itself.
*
* @function
* @name jQuery.sap.util.ResourceBundle.prototype.getText
* @public
*/
/**
* Enhances the resource bundle with a custom resource bundle. The bundle
* can be enhanced with multiple resource bundles. The last enhanced resource
* bundle wins against the previous ones and the original ones. This function
* can be called several times.
*
* @param {jQuery.sap.util.ResourceBundle} oBundle an instance of a <code>jQuery.sap.util.ResourceBundle</code>
* @since 1.16.5
* @private
*
* @function
* @name jQuery.sap.util.ResourceBundle.prototype._enhance
*/
/**
* A regular expression that describes language tags according to BCP-47.
* @see BCP47 "Tags for Identifying Languages" (http://www.ietf.org/rfc/bcp/bcp47.txt)
*
* The matching groups are
* 0=all
* 1=language (shortest ISO639 code + ext. language sub tags | 4digits (reserved) | registered language sub tags)
* 2=script (4 letters)
* 3=region (2letter language or 3 digits)
* 4=variants (separated by '-', Note: capturing group contains leading '-' to shorten the regex!)
* 5=extensions (including leading singleton, multiple extensions separated by '-')
* 6=private use section (including leading 'x', multiple sections separated by '-')
*
* [-------------------- language ----------------------][--- script ---][------- region --------][------------ variants --------------][--------- extensions --------------][------ private use -------]
*/
var rlocale = /^((?:[A-Z]{2,3}(?:-[A-Z]{3}){0,3})|[A-Z]{4}|[A-Z]{5,8})(?:-([A-Z]{4}))?(?:-([A-Z]{2}|[0-9]{3}))?(-[0-9A-Z]{5,8}|(?:[0-9][0-9A-Z]{3}))*(?:-([0-9A-WYZ](?:-[0-9A-Z]{2,8})+))*(?:-(X(?:-[0-9A-Z]{1,8})+))?$/i;
/**
* Resource bundles are stored according to the Java Development Kit conventions.
* JDK uses old language names for a few ISO639 codes ("iw" for "he", "ji" for "yi", "in" for "id" and "sh" for "sr").
* Make sure to convert newer codes to older ones before creating file names.
*/
var M_ISO639_NEW_TO_OLD = {
"he" : "iw",
"yi" : "ji",
"id" : "in",
"sr" : "sh"
};
var M_ISO639_OLD_TO_NEW = {
"iw" : "he",
"ji" : "yi",
"in" : "id",
"sn" : "sr"
};
/**
* HANA XS Engine can't handle private extensions in BCP47 language tags.
* Therefore, the agreed BCP47 codes for the technical languages 1Q and 2Q
* don't work as Accept-Header and need to be send as URL parameters as well.
* @private
*/
var M_SUPPORTABILITY_TO_XS = {
"en_US_saptrc" : "1Q",
"en_US_sappsd" : "2Q"
};
var rSAPSupportabilityLocales = /-(saptrc|sappsd)(?:-|$)/i;
/**
* Helper to normalize the given locale (in BCP-47 syntax) to the java.util.Locale format.
* @param {string} sLocale locale to normalize
* @return {string} Normalized locale or undefined if the locale can't be normalized
*/
function normalize(sLocale) {
var m;
if ( typeof sLocale === 'string' && (m = rlocale.exec(sLocale.replace(/_/g, '-'))) ) {
var sLanguage = m[1].toLowerCase();
sLanguage = M_ISO639_NEW_TO_OLD[sLanguage] || sLanguage;
var sScript = m[2] ? m[2].toLowerCase() : undefined;
var sRegion = m[3] ? m[3].toUpperCase() : undefined;
var sVariants = m[4];
var sPrivate = m[6];
// recognize and convert special SAP supportability locales (overwrites m[]!)
if ( (sPrivate && (m = rSAPSupportabilityLocales.exec(sPrivate)))
|| (sVariants && (m = rSAPSupportabilityLocales.exec(sVariants))) ) {
return "en_US_" + m[1].toLowerCase(); // for now enforce en_US (agreed with SAP SLS)
}
// Chinese: when no region but a script is specified, use default region for each script
if ( sLanguage === "zh" && !sRegion ) {
if ( sScript === "hans" ) {
sRegion = "CN";
} else if ( sScript === "hant" ) {
sRegion = "TW";
}
}
return sLanguage + (sRegion ? "_" + sRegion + (sVariants ? "_" + sVariants.slice(1).replace("-","_") : "") : "");
}
}
/**
* Returns the default locale (the locale defined in UI5 configuration if available, else "en")
* @return {string} The default locale
*/
function defaultLocale() {
var sLocale;
if (window.sap && sap.ui && sap.ui.getCore) {
sLocale = sap.ui.getCore().getConfiguration().getLanguage();
sLocale = normalize(sLocale);
}
return sLocale || "en";
}
/**
* Helper to normalize the given locale (java.util.Locale format) to the BCP-47 syntax.
* @param {string} sLocale locale to convert
* @return {string} Normalized locale or undefined if the locale can't be normalized
*/
function convertLocaleToBCP47(sLocale) {
var m;
if ( typeof sLocale === 'string' && (m = rlocale.exec(sLocale.replace(/_/g, '-'))) ) {
var sLanguage = m[1].toLowerCase();
sLanguage = M_ISO639_OLD_TO_NEW[sLanguage] || sLanguage;
return sLanguage + (m[3] ? "-" + m[3].toUpperCase() + (m[4] ? "-" + m[4].slice(1).replace("_","-") : "") : "");
}
}
/**
* A regular expression to split a URL into
* <ol>
* <li>a part before the file extension
* <li>the file extension itself
* <li>any remaining part after the file extension (query, hash - optional)
* </ol>.
*
* Won't match for URLs without a file extension.
*
* [------- prefix ------][----ext----][-------suffix--------]
* ?[--query--]#[--hash--]
*/
var rUrl = /^((?:[^?#]*\/)?[^\/?#]*)(\.[^.\/?#]+)((?:\?([^#]*))?(?:#(.*))?)$/;
/**
* List of supported file extensions.
*
* Could be enriched in future or even could be made
* extensible to support other formats as well.
*/
var aValidFileTypes = [ ".properties", ".hdbtextbundle"];
/**
* Helper to split a URL with the above regex.
* Either returns an object with the parts or undefined.
* @param {string} sUrl URL to analyze / split into pieces.
* @return {object} an object with properties for the individual URL parts
*/
function splitUrl(sUrl) {
var m = rUrl.exec(sUrl);
return m && { url : sUrl, prefix : m[1], ext : m[2], query: m[4], hash: (m[5] || ""), suffix : m[2] + (m[3] || "") };
}
/*
* Implements jQuery.sap.util.ResourceBundle
*/
var Bundle = function(sUrl, sLocale, bIncludeInfo, bAsync){
//last fallback is english if no or no valid locale is given
//TODO: If the browsers allow to access the users language preference this should be the fallback
this.sLocale = normalize(sLocale) || defaultLocale();
this.oUrlInfo = splitUrl(sUrl);
if ( !this.oUrlInfo || jQuery.inArray(this.oUrlInfo.ext, aValidFileTypes) < 0 ) {
throw new Error("resource URL '" + sUrl + "' has unknown type (should be one of " + aValidFileTypes.join(",") + ")");
}
this.bIncludeInfo = bIncludeInfo;
// list of custom bundles
this.aCustomBundles = [];
//declare list of property files that are loaded
this.aPropertyFiles = [];
this.aLocales = [];
//load the most specific property file
var p = load(this, this.sLocale, bAsync);
if (bAsync) {
this._promise = p;
}
};
Bundle.prototype = {};
/*
* Implements jQuery.sap.util.ResourceBundle.prototype._enhance
*/
Bundle.prototype._enhance = function(oCustomBundle) {
if (oCustomBundle && oCustomBundle instanceof Bundle) {
this.aCustomBundles.push(oCustomBundle);
} else {
// we report the error but do not break the execution
jQuery.sap.log.error("Custom ResourceBundle is either undefined or not an instanceof jQuery.sap.util.ResourceBundle. Therefore this custom ResourceBundle will be ignored!");
}
};
/*
* Implements jQuery.sap.util.ResourceBundle.prototype.getText
*/
Bundle.prototype.getText = function(sKey, aArgs, bCustomBundle){
var sValue = null,
i;
// loop over the custom bundles before resolving this one
// lookup the custom resource bundles (last one first!)
for (i = this.aCustomBundles.length - 1; i >= 0; i--) {
sValue = this.aCustomBundles[i].getText(sKey, aArgs, true /* bCustomBundle */);
// value found - so return it!
if (sValue != null) {
return sValue; // found!
}
}
//loop over all loaded property files and return the value for the key if any
for (i = 0; i < this.aPropertyFiles.length; i++) {
sValue = this.aPropertyFiles[i].getProperty(sKey);
if (typeof (sValue) === "string") {
break;
}
}
//value for this key was not found in the currently loaded property files,
//load the fallback locales
if (typeof (sValue) !== "string") {
var sTempLocale = this.aLocales[0];
while (sTempLocale.length > 0) {
// TODO: validate why, maybe remove? Introduced by Martin S.
// keep in sync with fallback mechanism in Java, ABAP (MIME & BSP)
// resource handler (Java: Peter M., MIME: Sebastian A., BSP: Silke A.)
if (sTempLocale == "zh_HK") {
sTempLocale = "zh_TW";
} else {
var p = sTempLocale.lastIndexOf('_');
if (p >= 0) {
sTempLocale = sTempLocale.substring(0,p);
} else if (sTempLocale != "en") {
sTempLocale = "en";
} else {
sTempLocale = "";
}
}
var oProperties = load(this, sTempLocale);
if (oProperties == null) {
continue;
}
//check whether the key is included in the newly loaded property file
sValue = oProperties.getProperty(sKey);
if (typeof (sValue) === "string") {
break;
}
}
}
if (!bCustomBundle && typeof (sValue) !== "string") {
jQuery.sap.assert(false, "could not find any translatable text for key '" + sKey + "' in bundle '" + this.oUrlInfo.url + "'");
sValue = sKey;
}
if (typeof (sValue) === "string") {
if (aArgs) {
sValue = jQuery.sap.formatMessage(sValue, aArgs);
}
if (this.bIncludeInfo) {
/* eslint-disable no-new-wrappers */
sValue = new String(sValue);
/* eslint-enable no-new-wrappers */
sValue.originInfo = {
source: "Resource Bundle",
url: this.oUrlInfo.url,
locale: this.sLocale,
key: sKey
};
}
}
return sValue;
};
/*
* If a .properties file for the given locale is not loaded yet
* in the given bundle, this method loads the .properties file and
* adds it to the bundle.
* @param {string} sLocale the text to parse
* @param oBundle the resource bundle to extend
* @param bAsync whether the resource should be loaded asynchronously. A Promise is returned in this case
* @return The newly loaded properties or <code>null</code>
* when the properties for the given locale already loaded.
* @private
*/
function load(oBundle, sLocale, bAsync) {
var oUrl = oBundle.oUrlInfo,
sUrl,
oRequest,
oProperties,
oPromise;
if ( jQuery.inArray(sLocale, oBundle.aLocales) == -1 ) {
if ( shouldRequest(sLocale) ) {
switch (oUrl.ext) {
case '.hdbtextbundle':
if ( M_SUPPORTABILITY_TO_XS[sLocale] ) {
// Add technical support languages also as URL parameter (as XS engine can't handle private extensions in Accept-Language header)
sUrl = oUrl.prefix + oUrl.suffix + '?' + (oUrl.query ? oUrl.query + "&" : "") + "sap-language=" + M_SUPPORTABILITY_TO_XS[sLocale] + (oUrl.hash ? "#" + oUrl.hash : "");
} else {
sUrl = oUrl.url;
}
oRequest = {
url: sUrl,
// Alternative: add locale as query:
// url: oUrl.prefix + oUrl.suffix + '?' + (oUrl.query ? oUrl.query + "&" : "") + "locale=" + sLocale + (oUrl.hash ? "#" + oUrl.hash : ""),
headers : {
"Accept-Language": convertLocaleToBCP47(sLocale) || ""
}
};
break;
default:
oRequest = {
url: oUrl.prefix + (sLocale ? "_" + sLocale : "") + oUrl.suffix
};
break;
}
if (bAsync) {
oRequest.async = true;
oPromise = Promise.resolve(jQuery.sap.properties(oRequest));
} else {
oProperties = jQuery.sap.properties(oRequest);
}
} else {
// dummy result (empty)
oProperties = {
getProperty : function() {
return undefined;
}
};
if (bAsync) {
oPromise = Promise.resolve(oProperties);
}
}
// remember result and locales that have been loaded so far (to avoid repeated roundtrips)
if (bAsync) {
oPromise.then(function(oProps){
oBundle.aPropertyFiles.push(oProps);
oBundle.aLocales.push(sLocale);
});
return oPromise;
} else {
oBundle.aPropertyFiles.push(oProperties);
oBundle.aLocales.push(sLocale);
return oProperties;
}
}
return bAsync ? Promise.resolve(null) : null;
}
function shouldRequest(sLocale) {
var aLanguages = window.sap && sap.ui && sap.ui.getCore && sap.ui.getCore().getConfiguration().getSupportedLanguages();
if ( aLanguages && aLanguages.length > 0 ) {
return jQuery.inArray(sLocale, aLanguages) >= 0;
}
return true;
}
/**
* Creates and returns a new instance of {@link jQuery.sap.util.ResourceBundle}
* using the given URL and locale to determine what to load.
*
* @public
* @param {object} [mParams] Parameters used to initialize the resource bundle
* @param {string} [mParams.url=''] The URL to the base .properties file of a bundle (.properties file without any locale information, e.g. "mybundle.properties")
* @param {string} [mParams.locale='en'] Optional string of the language and an optional country code separated by underscore (e.g. "en_GB" or "fr")
* @param {boolean} [mParams.includeInfo=false] Optional boolean whether to include origin information into the returned property values
* @param {boolean} [mParams.async=false] Optional boolean whether first bundle should be loaded asynchronously. Note: fallback bundles will still be loaded synchronously afterwards if needed.
* @return {jQuery.sap.util.ResourceBundle|Promise} A new resource bundle instance or a ECMA Script 6 Promise (in asynchronous case)
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.resources = function resources(mParams) {
mParams = jQuery.extend({url: "", locale: undefined, includeInfo: false}, mParams);
var bAsync = !!mParams.async;
var oBundle = new Bundle(mParams.url, mParams.locale, mParams.includeInfo, bAsync);
if (bAsync) {
return new Promise(function(resolve, reject){
function _resolve(){
resolve(oBundle);
delete oBundle._promise;
}
oBundle._promise.then(_resolve, _resolve);
});
} else {
return oBundle;
}
};
jQuery.sap.resources._getFallbackLocales = function(sLocale, aSupportedLocales) {
var sTempLocale = normalize(sLocale),
aLocales = [];
function supported(sLocale) {
return !aSupportedLocales || aSupportedLocales.length === 0 || jQuery.inArray(sLocale, aSupportedLocales) >= 0;
}
while (sTempLocale) {
if ( supported(sTempLocale) ) {
aLocales.push(sTempLocale);
}
// TODO: validate why, maybe remove? Introduced by Martin S.
// keep in sync with fallback mechanism in Java, ABAP (MIME & BSP)
// resource handler (Java: Peter M., MIME: Sebastian A., BSP: Silke A.)
if ( sTempLocale === "zh_HK" ) {
sTempLocale = "zh_TW";
} else {
var p = sTempLocale.lastIndexOf('_');
if (p > 0 ) {
sTempLocale = sTempLocale.slice(0, p);
} else if ( sTempLocale !== "en" ) {
sTempLocale = "en";
} else {
sTempLocale = "";
}
}
}
if ( supported("") ) {
aLocales.push("");
}
return aLocales;
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.script.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides miscellaneous utility functions that might be useful for any script
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Some private variable used for creation of (pseudo-)unique ids.
* @type integer
* @private
*/
var iIdCounter = 0;
/**
* Creates and returns a pseudo-unique id.
*
* No means for detection of overlap with already present or future UIDs.
*
* @return {string} A pseudo-unique id.
* @public
*/
jQuery.sap.uid = function uid() {
return "id-" + new Date().valueOf() + "-" + iIdCounter++;
};
/**
* Calls a method after a given delay and returns an id for this timer
*
* @param {int} iDelay Delay time in milliseconds
* @param {object} oObject Object from which the method should be called
* @param {string|object} method function pointer or name of the method
* @param {array} [aParameters] Method parameters
* @return {string} Id which can be used to cancel the timer with clearDelayedCall
* @public
*/
jQuery.sap.delayedCall = function delayedCall(iDelay, oObject, method, aParameters) {
return setTimeout(function(){
if (jQuery.type(method) == "string") {
method = oObject[method];
}
method.apply(oObject, aParameters || []);
}, iDelay);
};
/**
* Stops the delayed call.
*
* The function given when calling delayedCall is not called anymore.
*
* @param {string} sDelayedCallId The id returned, when calling delayedCall
* @public
*/
jQuery.sap.clearDelayedCall = function clearDelayedCall(sDelayedCallId) {
clearTimeout(sDelayedCallId);
return this;
};
/**
* Calls a method after a given interval and returns an id for this interval.
*
* @param {int} iInterval Interval time in milliseconds
* @param {object} oObject Object from which the method should be called
* @param {string|object} method function pointer or name of the method
* @param {array} [aParameters] Method parameters
* @return {string} Id which can be used to cancel the interval with clearIntervalCall
* @public
*/
jQuery.sap.intervalCall = function intervalCall(iInterval, oObject, method, aParameters) {
return setInterval(function(){
if (jQuery.type(method) == "string") {
method = oObject[method];
}
method.apply(oObject, aParameters || []);
}, iInterval);
};
/**
* Stops the interval call.
*
* The function given when calling intervalCall is not called anymore.
*
* @param {string} sIntervalCallId The id returned, when calling intervalCall
* @public
*/
jQuery.sap.clearIntervalCall = function clearIntervalCall(sIntervalCallId) {
clearInterval(sIntervalCallId);
return this;
};
// Javadoc for private inner class "UriParams" - this list of comments is intentional!
/**
* @interface Encapsulates all URI parameters of the current windows location (URL).
*
* Use {@link jQuery.sap.getUriParameters} to create an instance of jQuery.sap.util.UriParameters.
*
* @author SAP SE
* @version 1.28.5
* @since 0.9.0
* @name jQuery.sap.util.UriParameters
* @public
*/
/**
* Returns the value(s) of the URI parameter with the given name sName.
*
* If the boolean parameter bAll is <code>true</code>, an array of string values of all
* occurrences of the URI parameter with the given name is returned. This array is empty
* if the URI parameter is not contained in the windows URL.
*
* If the boolean parameter bAll is <code>false</code> or is not specified, the value of the first
* occurrence of the URI parameter with the given name is returned. Might be <code>null</code>
* if the URI parameter is not contained in the windows URL.
*
* @param {string} sName The name of the URI parameter.
* @param {boolean} [bAll=false] Optional, specifies whether all or only the first parameter value should be returned.
* @return {string|array} The value(s) of the URI parameter with the given name
* @SecSource {return|XSS} Return value contains URL parameters
*
* @function
* @name jQuery.sap.util.UriParameters.prototype.get
*/
/*
* Implements jQuery.sap.util.UriParameters
*/
var UriParams = function(sUri) {
this.mParams = {};
var sQueryString = sUri || window.location.href;
if ( sQueryString.indexOf('#') >= 0 ) {
sQueryString = sQueryString.slice(0, sQueryString.indexOf('#'));
}
if (sQueryString.indexOf("?") >= 0) {
sQueryString = sQueryString.slice(sQueryString.indexOf("?") + 1);
var aParameters = sQueryString.split("&"),
mParameters = {},
aParameter,
sName,
sValue;
for (var i = 0; i < aParameters.length; i++) {
aParameter = aParameters[i].split("=");
sName = decodeURIComponent(aParameter[0]);
sValue = aParameter.length > 1 ? decodeURIComponent(aParameter[1].replace(/\+/g,' ')) : "";
if (sName) {
if (!Object.prototype.hasOwnProperty.call(mParameters, sName)) {
mParameters[sName] = [];
}
mParameters[sName].push(sValue);
}
}
this.mParams = mParameters;
}
};
UriParams.prototype = {};
/*
* Implements jQuery.sap.util.UriParameters.prototype.get
*/
UriParams.prototype.get = function(sName, bAll) {
var aValues = Object.prototype.hasOwnProperty.call(this.mParams, sName) ? this.mParams[sName] : [];
return bAll === true ? aValues : (aValues[0] || null);
};
/**
* Creates and returns a new instance of {@link jQuery.sap.util.UriParameters}.
*
* Example for reading a single URI parameter (or the value of the first
* occurrence of the URI parameter):
* <pre>
* var sValue = jQuery.sap.getUriParameters().get("myUriParam");
* </pre>
*
* Example for reading the values of the first of the URI parameter
* (with multiple occurrences):
* <pre>
* var aValues = jQuery.sap.getUriParameters().get("myUriParam", true);
* for(i in aValues){
* var sValue = aValues[i];
* }
* </pre>
*
* @public
* @param {string} sUri Uri to determine the parameters for
* @return {jQuery.sap.util.UriParameters} A new URI parameters instance
*/
jQuery.sap.getUriParameters = function getUriParameters(sUri) {
return new UriParams(sUri);
};
/**
* Sorts the given array in-place and removes any duplicates (identified by "===").
*
* Use <code>jQuery.unique()</code> for arrays of DOMElements.
*
* @param {Array} a An Array of any type
* @return {Array} Same array as given (for chaining)
* @public
*/
jQuery.sap.unique = function(a) {
jQuery.sap.assert(a instanceof Array, "unique: a must be an array");
var l = a.length;
if ( l > 1 ) {
a.sort();
var j = 0;
for (var i = 1; i < l; i++) {
// invariant: i is the entry to check, j is the last unique entry known so far
if ( a[i] !== a[j] ) {
a[++j] = a[i];
}
}
// cut off the rest - if any
if ( ++j < l ) {
a.splice(j, l - j);
}
}
return a;
};
/**
* Compares the two given values for equality, especially takes care not to compare
* arrays and objects by reference, but compares their content.
* Note: function does not work with comparing XML objects
*
* @param {any} a A value of any type
* @param {any} b A value of any type
* @param {int} [maxDepth=10] Maximum recursion depth
* @param {boolean} [contains] Whether all existing properties in a are equal as in b
*
* @return {boolean} Whether a and b are equal
* @public
*/
jQuery.sap.equal = function(a, b, maxDepth, contains, depth) {
// Optional parameter normalization
if (typeof maxDepth == "boolean") {
contains = maxDepth;
maxDepth = undefined;
}
if (!depth) {
depth = 0;
}
if (!maxDepth) {
maxDepth = 10;
}
if (depth > maxDepth) {
return false;
}
if (a === b) {
return true;
}
if (jQuery.isArray(a) && jQuery.isArray(b)) {
if (!contains) {
if (a.length != b.length) {
return false;
}
} else {
if (a.length > b.length) {
return false;
}
}
for (var i = 0; i < a.length; i++) {
if (!jQuery.sap.equal(a[i], b[i], maxDepth, contains, depth + 1)) {
return false;
}
}
return true;
}
if (typeof a == "object" && typeof b == "object") {
if (!a || !b) {
return false;
}
if (a.constructor != b.constructor) {
return false;
}
if (a.nodeName && b.nodeName && a.namespaceURI && b.namespaceURI) {
return jQuery.sap.isEqualNode(a,b);
}
if (a instanceof Date) {
return a.valueOf() == b.valueOf();
}
for (var i in a) {
if (!jQuery.sap.equal(a[i], b[i], maxDepth, contains, depth + 1)) {
return false;
}
}
if (!contains) {
for (var i in b) {
if (a[i] === undefined) {
return false;
}
}
}
return true;
}
return false;
};
/**
* Iterates over elements of the given object or array.
*
* Works similar to <code>jQuery.each</code>, but a numeric index is only used for
* instances of <code>Array</code>. For all other objects, including those with a numeric
* <code>length</code> property, the properties are iterated by name.
*
* The contract for the <code>fnCallback</code> is the same as for <code>jQuery.each</code>,
* when it returns <code>false</code>, then the iteration stops (break).
*
* @param {object|any[]} oObject object or array to enumerate the properties of
* @param {function} fnCallback function to call for each property name
* @return {object|any[]} the given <code>oObject</code>
* @since 1.11
*/
jQuery.sap.each = function(oObject, fnCallback) {
var isArray = jQuery.isArray(oObject),
length, i;
if ( isArray ) {
for (i = 0, length = oObject.length; i < length; i++) {
if ( fnCallback.call(oObject[i], i, oObject[i]) === false ) {
break;
}
}
} else {
for ( i in oObject ) {
if ( fnCallback.call(oObject[i], i, oObject[i] ) === false ) {
break;
}
}
}
return oObject;
};
/**
* Substitute for <code>for(n in o)</code> loops which fixes the 'Don'tEnum' bug of IE8.
*
* Iterates over all enumerable properties of the given object and calls the
* given callback function for each of them. The assumed signature of the
* callback function is
*
* fnCallback(name, value)
*
* where name is the name of the property and value is its value.
*
* When an object in IE8 overrides a property of Object.prototype
* that has been marked as 'don't enum', then IE8 by mistake also
* doesn't enumerate the overriding property.
*
* A 100% complete substitute is hard to achieve. The current implementation
* enumerates an overridden property when it either is an 'own' property
* (hasOwnProperty(name) is true) or when the property value is different
* from the value in the Object.prototype object.
*
* @param {object} oObject object to enumerate the properties of
* @param {function} fnCallback function to call for each property name
* @function
* @since 1.7.1
*/
jQuery.sap.forIn = {toString:null}.propertyIsEnumerable("toString") ?
// for browsers without the bug we use the straight forward implementation of a for in loop
function(oObject, fnCallback) {
for (var n in oObject) {
if ( fnCallback(n, oObject[n]) === false ) {
return;
}
}
} :
// use a special implementation for IE8
(function() {
var DONT_ENUM_KEYS = ["toString","valueOf","toLocaleString", "hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],
DONT_ENUM_KEYS_LENGTH = DONT_ENUM_KEYS.length,
oObjectPrototype = Object.prototype,
fnHasOwnProperty = oObjectPrototype.hasOwnProperty;
return function(oObject, fnCallback) {
var n,i;
// standard for(in) loop
for (n in oObject) {
if ( fnCallback(n, oObject[n]) === false ) {
return;
}
}
// additionally check the known 'don't enum' names
for (var i = 0; i < DONT_ENUM_KEYS_LENGTH; i++) {
n = DONT_ENUM_KEYS[i];
// assume an enumerable property if it is either an own property
// or if its value differes fro mthe value in the Object.prototype
if ( fnHasOwnProperty.call(oObject,n) || oObject[n] !== oObjectPrototype[n] ) {
if ( fnCallback(n, oObject[n]) === false ) {
return;
}
}
}
// Note: this substitute implementation still fails in several regards
// - it fails when oObject is identical to Object.prototype (iterates non-enumerable properties)
// - it fails when one of the don't enum properties by intention has been overridden in the
// prototype chain with a value identical to the value in Object.prototype
// - the don't enum properties are handled out of order. This is okay with the ECMAScript
// spec but might be unexpected for some callers
};
}());
/**
* Calculate delta of old list and new list
* This implements the algorithm described in "A Technique for Isolating Differences Between Files"
* (Commun. ACM, April 1978, Volume 21, Number 4, Pages 264-268)
* @public
* @param {Array} aOld Old Array
* @param {Array} aNew New Array
* @param {function} [fnCompare] Function to compare list entries
* @param {boolean} [bUniqueEntries] Whether entries are unique, so no duplicate entries exist
* @return {Array} List of changes
*/
jQuery.sap.arrayDiff = function(aOld, aNew, fnCompare, bUniqueEntries){
fnCompare = fnCompare || function(vValue1, vValue2) {
return jQuery.sap.equal(vValue1, vValue2);
};
var aOldRefs = [];
var aNewRefs = [];
//Find references
var aMatches = [];
for (var i = 0; i < aNew.length; i++) {
var oNewEntry = aNew[i];
var iFound = 0;
var iTempJ;
// if entries are unique, first check for whether same index is same entry
// and stop searching as soon the first matching entry is found
if (bUniqueEntries && fnCompare(aOld[i], oNewEntry)) {
iFound = 1;
iTempJ = i;
} else {
for (var j = 0; j < aOld.length; j++) {
if (fnCompare(aOld[j], oNewEntry)) {
iFound++;
iTempJ = j;
if (bUniqueEntries || iFound > 1) {
break;
}
}
}
}
if (iFound == 1) {
var oMatchDetails = {
oldIndex: iTempJ,
newIndex: i
};
if (aMatches[iTempJ]) {
delete aOldRefs[iTempJ];
delete aNewRefs[aMatches[iTempJ].newIndex];
} else {
aNewRefs[i] = {
data: aNew[i],
row: iTempJ
};
aOldRefs[iTempJ] = {
data: aOld[iTempJ],
row: i
};
aMatches[iTempJ] = oMatchDetails;
}
}
}
//Pass 4: Find adjacent matches in ascending order
for (var i = 0; i < aNew.length - 1; i++) {
if (aNewRefs[i] &&
!aNewRefs[i + 1] &&
aNewRefs[i].row + 1 < aOld.length &&
!aOldRefs[aNewRefs[i].row + 1] &&
fnCompare(aOld[ aNewRefs[i].row + 1 ], aNew[i + 1])) {
aNewRefs[i + 1] = {
data: aNew[i + 1],
row: aNewRefs[i].row + 1
};
aOldRefs[aNewRefs[i].row + 1] = {
data: aOldRefs[aNewRefs[i].row + 1],
row: i + 1
};
}
}
//Pass 5: Find adjacent matches in descending order
for (var i = aNew.length - 1; i > 0; i--) {
if (aNewRefs[i] &&
!aNewRefs[i - 1] &&
aNewRefs[i].row > 0 &&
!aOldRefs[aNewRefs[i].row - 1] &&
fnCompare(aOld[aNewRefs[i].row - 1], aNew[i - 1])) {
aNewRefs[i - 1] = {
data: aNew[i - 1],
row: aNewRefs[i].row - 1
};
aOldRefs[aNewRefs[i].row - 1] = {
data: aOldRefs[aNewRefs[i].row - 1],
row: i - 1
};
}
}
//Pass 6: Generate diff data
var aDiff = [];
if (aNew.length == 0) {
//New list is empty, all items were deleted
for (var i = 0; i < aOld.length; i++) {
aDiff.push({
index: 0,
type: 'delete'
});
}
} else {
var iNewListIndex = 0;
if (!aOldRefs[0]) {
//Detect all deletions at the beginning of the old list
for (var i = 0; i < aOld.length && !aOldRefs[i]; i++) {
aDiff.push({
index: 0,
type: 'delete'
});
iNewListIndex = i + 1;
}
}
for (var i = 0; i < aNew.length; i++) {
if (!aNewRefs[i] || aNewRefs[i].row > iNewListIndex) {
//Entry doesn't exist in old list = insert
aDiff.push({
index: i,
type: 'insert'
});
} else {
iNewListIndex = aNewRefs[i].row + 1;
for (var j = aNewRefs[i].row + 1; j < aOld.length && (!aOldRefs[j] || aOldRefs[j].row < i); j++) {
aDiff.push({
index: i + 1,
type: 'delete'
});
iNewListIndex = j + 1;
}
}
}
}
return aDiff;
};
/**
* A factory returning a tokenizer object for JS values.
* Contains functions to consume tokens on an input string.
* @private
* @returns {object} - the tokenizer
*/
jQuery.sap._createJSTokenizer = function() {
var at, // The index of the current character
ch, // The current character
escapee = {
'"': '"',
'\'': '\'',
'\\': '\\',
'/': '/',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
},
text,
error = function(m) {
// Call error when something is wrong.
throw {
name: 'SyntaxError',
message: m,
at: at,
text: text
};
},
next = function(c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
},
number = function() {
// Parse a number value.
var number, string = '';
if (ch === '-') {
string = '-';
next('-');
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
if (ch === '.') {
string += '.';
while (next() && ch >= '0' && ch <= '9') {
string += ch;
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
next();
if (ch === '-' || ch === '+') {
string += ch;
next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
}
number = +string;
if (!isFinite(number)) {
error("Bad number");
} else {
return number;
}
},
string = function() {
// Parse a string value.
var hex, i, string = '', quote,
uffff;
// When parsing for string values, we must look for " and \ characters.
if (ch === '"' || ch === '\'') {
quote = ch;
while (next()) {
if (ch === quote) {
next();
return string;
}
if (ch === '\\') {
next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else {
string += ch;
}
}
}
error("Bad string");
},
name = function() {
// Parse a name value.
var name = '',
allowed = function(ch) {
return ch === "_" ||
(ch >= "0" && ch <= "9") ||
(ch >= "a" && ch <= "z") ||
(ch >= "A" && ch <= "Z");
};
if (allowed(ch)) {
name += ch;
} else {
error("Bad name");
}
while (next()) {
if (ch === ' ') {
next();
return name;
}
if (ch === ':') {
return name;
}
if (allowed(ch)) {
name += ch;
} else {
error("Bad name");
}
}
error("Bad name");
},
white = function() {
// Skip whitespace.
while (ch && ch <= ' ') {
next();
}
},
word = function() {
// true, false, or null.
switch (ch) {
case 't':
next('t');
next('r');
next('u');
next('e');
return true;
case 'f':
next('f');
next('a');
next('l');
next('s');
next('e');
return false;
case 'n':
next('n');
next('u');
next('l');
next('l');
return null;
}
error("Unexpected '" + ch + "'");
},
value, // Place holder for the value function.
array = function() {
// Parse an array value.
var array = [];
if (ch === '[') {
next('[');
white();
if (ch === ']') {
next(']');
return array; // empty array
}
while (ch) {
array.push(value());
white();
if (ch === ']') {
next(']');
return array;
}
next(',');
white();
}
}
error("Bad array");
},
object = function() {
// Parse an object value.
var key, object = {};
if (ch === '{') {
next('{');
white();
if (ch === '}') {
next('}');
return object; // empty object
}
while (ch) {
if (ch >= "0" && ch <= "9") {
key = number();
} else if (ch === '"' || ch === '\'') {
key = string();
} else {
key = name();
}
white();
next(':');
if (Object.hasOwnProperty.call(object, key)) {
error('Duplicate key "' + key + '"');
}
object[key] = value();
white();
if (ch === '}') {
next('}');
return object;
}
next(',');
white();
}
}
error("Bad object");
};
value = function() {
// Parse a JS value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case '{':
return object();
case '[':
return array();
case '"':
case '\'':
return string();
case '-':
return number();
default:
return ch >= '0' && ch <= '9' ? number() : word();
}
};
// Return the parse function. It will have access to all of the above
// functions and variables.
function parseJS(source, start) {
var result;
text = source;
at = start || 0;
ch = ' ';
result = value();
if ( isNaN(start) ) {
white();
if (ch) {
error("Syntax error");
}
return result;
} else {
return { result : result, at : at - 1 };
}
}
return {
array: array,
error: error,
/**
* Returns the index of the current character.
* @returns {number} The current character's index.
*/
getIndex: function() {
return at - 1;
},
getCh: function() {
return ch;
},
init: function(source, iIndex) {
text = source;
at = iIndex || 0;
ch = ' ';
},
name: name,
next: next,
number: number,
parseJS: parseJS,
/**
* Advances the index in the text to <code>iIndex</code>. Fails if the new index
* is smaller than the previous index.
*
* @param {number} iIndex - the new index
*/
setIndex: function(iIndex) {
if (iIndex < at - 1) {
throw new Error("Must not set index " + iIndex
+ " before previous index " + (at - 1));
}
at = iIndex;
next();
},
string: string,
value: value,
white: white,
word: word
};
};
/**
* Parse simple JS objects.
*
* A parser for JS object literals. This is different from a JSON parser, as it does not have
* the JSON specification as a format description, but a subset of the JavaScript language.
* The main difference is, that keys in objects do not need to be quoted and strings can also
* be defined using apostrophes instead of quotation marks.
*
* The parser does not support functions, but only boolean, number, string, object and array.
*
* @param {string} The string containing the JS objects
* @throws an error, if the string does not contain a valid JS object
* @returns {object} the JS object
*
* @since 1.11
*/
jQuery.sap.parseJS = jQuery.sap._createJSTokenizer().parseJS;
/**
* Merge the contents of two or more objects together into the first object.
* Usage is the same as jQuery.extend, but Arguments that are null or undefined are NOT ignored.
*
* @since 1.26
*/
jQuery.sap.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
for ( ; i < length; i++ ) {
options = arguments[ i ];
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.sap.extend( deep, clone, copy );
} else {
target[ name ] = copy;
}
}
}
// Return the modified object
return target;
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.sjax.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*
* Provides convenience functions for synchronous communication, based on the jQuery.ajax() function.
*/
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
jQuery.sap.sjaxSettings = {
/**
* Whether to return an object consisting of data and status and error codes or only the simple data
*/
complexResult: true,
/**
* fallback value when complexResult is set to false and an error occurred. Then fallback will be returned.
*/
fallback: undefined
};
/**
* Convenience wrapper around <code>jQuery.ajax()</code> that avoids the need for callback functions when
* synchronous calls are made. If the setting <code>complexResult</code> is true (default), then the return value
* is an object with the following properties
* <ul>
* <li><code>success</code> boolean whether the call succeeded or not
* <li><code>data</code> any the data returned by the call. For dataType 'text' this is a string,
* for JSON it is an object, for XML it is a document. When the call failed, then data is not defined
* <li><code>status</code> string a textual status ('success,', 'error', 'timeout',...)
* <li><code>statusCode</code> string the HTTP status code of the request
* <li><code>error</code> Error an error object (exception) in case an error occurred
* </ul>
*
* When <code>complexResult</code> is false, then in the case of success, only 'data' is returned, in case of an error the
* 'fallback' setting is returned (defaults to undefined).
*
* Note that async=false is always enforced by this method.
*
* @param {string} oOrigSettings the ajax() settings
* @return result, see above
*
* @public
* @since 0.9.0
* @SecSink {0|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.sjax = function sjax(oOrigSettings) {
var s = jQuery.extend(true, {}, jQuery.sap.sjaxSettings, oOrigSettings,
// the following settings are enforced as this is the rightmost object in the extend call
{
async: false,
success : function(data, textStatus, xhr) {
// oResult = { success : true, data : data, status : textStatus, statusCode : xhr.status };
oResult = { success : true, data : data, status : textStatus, statusCode : xhr && xhr.status };
},
error : function(xhr, textStatus, error) {
oResult = { success : false, data : undefined, status : textStatus, error : error, statusCode : xhr.status };
}
});
var oResult;
jQuery.ajax(s);
if (!s.complexResult) {
return oResult.success ? oResult.data : s.fallback;
}
return oResult;
};
/**
* Convenience wrapper that checks whether a given web resource could be accessed.
* @SecSink {0|PATH} Parameter is used for future HTTP requests
* @SecSource {return} Returned value is under control of an external resource
*/
jQuery.sap.syncHead = function(sUrl) {
return jQuery.sap.sjax({type:'HEAD', url: sUrl}).success;
};
/**
* Convenience wrapper for {@link jQuery.sap.sjax} that enforeces the Http method GET and defaults the
* data type of the result to 'text'.
*
* @param {string} sUrl the URL
* @param {string|object} data request parameters in the format accepted by jQuery.ajax()
* @param {string} [sDataType='text'] the type of data expected from the server, default is "text"
* @return result @see jQuery.sap.sjax
*
* @public
* @since 0.9.0
* @SecSink {0 1|PATH} Parameter is used for future HTTP requests
* @SecSource {return} Returned value is under control of an external resource
*/
jQuery.sap.syncGet = function syncGet(sUrl, data, sDataType) {
return jQuery.sap.sjax({
url: sUrl,
data: data,
type: 'GET',
dataType: sDataType || 'text'
});
};
/**
* Convenience wrapper for {@link jQuery.sap.sjax} that enforces the Http method POST and defaults the
* data type of the result to 'text'.
*
* @param {string} sUrl the URL
* @param {string|object} data request parameters in the format accepted by jQuery.ajax()
* @param {string} [sDataType='text'] the type of data expected from the server, default is "text"
* @return result @see jQuery.sap.sjax
*
* @public
* @since 0.9.0
* @SecSink {0 1|PATH} Parameter is used for future HTTP requests
* @SecSource {return} Returned value is under control of an external resource
*/
jQuery.sap.syncPost = function syncPost(sUrl, data, sDataType) {
return jQuery.sap.sjax({
url: sUrl,
data: data,
type: 'POST',
dataType: sDataType || 'text'
});
};
/**
* Convenience wrapper for {@link jQuery.sap.sjax} that enforces the Http method GET and the data type 'text'.
* If a fallback value is given, the function simply returns the response as a text or - if some error occurred -
* the fallback value. This is useful for applications that don't require detailed error diagnostics.
*
* If applications need to know about occurring errors, they can either call <code>sjax()</code> directly
* or they can omit the fallback value (providing only two parameters to syncGetText()).
* They then receive the same complex result object as for the sjax() call.
*
* @param {string} sUrl the URL
* @param {string|object} data request parameters in the format accepted by jQuery.ajax()
* @param {string} [fallback] if set, only data is returned (and this fallback instead in case of errors); if unset, a result structure is returned
* @return result @see jQuery.sap.sjax
*
* @public
* @since 0.9.0
* @SecSink {0 1|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.syncGetText = function syncGetText(sUrl, data, fallback) {
return jQuery.sap.sjax({
url: sUrl,
data: data,
type: 'GET',
dataType: 'text',
fallback: fallback,
complexResult : (arguments.length < 3)
});
};
/**
* Convenience wrapper for {@link jQuery.sap.sjax} that enforces the Http method GET and the data type 'json'.
* If a fallback value is given, the function simply returns the response as an object or - if some error occurred -
* the fallback value. This is useful for applications that don't require detailed error diagnostics.
*
* If applications need to know about occurring errors, they can either call <code>sjax()</code> directly
* or they can omit the fallback value (providing only two parameters to syncGetJSON()).
* They then receive the same complex result object as for the sjax() call.
*
* Note that providing "undefined" or "null" as a fallback is different from omitting the fallback (complex result).
*
* @param {string} sUrl the URL
* @param {string|object} data request parameters in the format accepted by jQuery.ajax()
* @param {object} [fallback] if set, only data is returned (and this fallback instead in case of errors); if unset, a result structure is returned
* @return result @see jQuery.sap.sjax
*
* @public
* @since 0.9.0
* @SecSink {0 1|PATH} Parameter is used for future HTTP requests
*/
jQuery.sap.syncGetJSON = function syncGetJSON(sUrl, data, fallback) {
return jQuery.sap.sjax({
url: sUrl,
data: data || null,
type: 'GET',
dataType: 'json',
fallback: fallback,
complexResult : (arguments.length < 3)
});
};
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.strings.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides useful string operations not available in pure JavaScript.
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Checks whether a given sString ends with sEndString
* respecting the case of the strings.
*
* @param {string} sString The string to be checked
* @param {string} sEndString The end string to be searched
* @return True if sString ends with sEndString
* @type {boolean}
* @see jQuery.sap.endsWithIgnoreCase
* @public
*/
jQuery.sap.endsWith = function endsWith(sString, sEndString) {
if (typeof (sEndString) != "string" || sEndString == "") {
return false;
}
var iPos = sString.lastIndexOf(sEndString);
return iPos >= 0 && iPos == sString.length - sEndString.length;
};
/**
* Checks whether a given sString ends with sEndString
* ignoring the case of the strings.
*
* @param {string} sString the string to be checked
* @param {string} sEndString the end string to be searched
* @return true if sString ends with sEndString
* @type {boolean}
* @see jQuery.sap.endsWith
* @public
*/
jQuery.sap.endsWithIgnoreCase = function endsWithIgnoreCase(sString, sEndString) {
if (typeof (sEndString) != "string" || sEndString == "") {
return false;
}
sString = sString.toUpperCase();
sEndString = sEndString.toUpperCase();
return jQuery.sap.endsWith(sString,sEndString);
};
/**
* Checks whether a given sString starts with sStartString
* respecting the case of the strings.
*
* @param {string} sString The string to be checked
* @param {string} sStartString The start string to be searched
* @return True if sString ends with sEndString
* @type {boolean}
* @see jQuery.sap.startsWithIgnoreCase
* @public
*/
jQuery.sap.startsWith = function startsWith(sString, sStartString) {
if (typeof (sStartString) != "string" || sStartString == "") {
return false;
}
if (sString == sStartString) {
return true;
}
return sString.indexOf(sStartString) == 0;
};
/**
* Checks whether a given sString starts with sStartString
* ignoring the case of the strings.
*
* @param {string} sString The string to be checked
* @param {string} sStartString The start string to be searched
* @return True if sString ends with sEndString
* @type {boolean}
* @see jQuery.sap.startsWith
* @public
*/
jQuery.sap.startsWithIgnoreCase = function startsWithIgnoreCase(sString, sStartString) {
if (typeof (sStartString) != "string" || sStartString == "") {
return false;
}
sString = sString.toUpperCase();
sStartString = sStartString.toUpperCase();
return jQuery.sap.startsWith(sString,sStartString);
};
/**
* Converts a character of the string to upper case.<br/>
* If no pos is defined as second parameter or pos is negative or greater than sString the first character will be
* converted into upper case. the first char position is 0.
*
* @param {string} sString The string to be checked
* @param {int} iPos the position of the character that will be uppercase
* @return The string with the firstletter in upper case
* @type {string}
* @public
* @SecPassthrough {0|return}
*/
jQuery.sap.charToUpperCase = function charToUpperCase(sString,iPos) {
if (!sString) {
return sString;
}
if (!iPos || isNaN(iPos) || iPos <= 0 || iPos >= sString.length) {
iPos = 0;
}
var sChar = sString.charAt(iPos).toUpperCase();
if (iPos > 0) {
return sString.substring(0,iPos) + sChar + sString.substring(iPos + 1);
}
return sChar + sString.substring(iPos + 1);
};
/**
* Pads a string on the left side until is has the given length.<br/>
*
* @param {string} sString The string to be padded
* @param {string} sPadChar The char to use for the padding
* @param {int} iLength the target length of the string
* @return The padded string
* @type {string}
* @public
* @SecPassthrough {0 1|return}
*/
jQuery.sap.padLeft = function padLeft(sString, sPadChar, iLength) {
if (!sString) {
sString = "";
}
while (sString.length < iLength) {
sString = sPadChar + sString;
}
return sString;
};
/**
* Pads a string on the right side until is has the given length.<br/>
*
* @param {string} sString The string to be padded
* @param {string} sPadChar The char to use for the padding
* @param {int} iLength the target length of the string
* @return The padded string
* @type {string}
* @public
* @SecPassthrough {0 1|return}
*/
jQuery.sap.padRight = function padRight(sString, sPadChar, iLength) {
if (!sString) {
sString = "";
}
while (sString.length < iLength) {
sString = sString + sPadChar;
}
return sString;
};
var rCamelCase = /-(.)/ig;
/**
* Transforms a hyphen separated string to an camel case string.
*
* @param {string} sString Hyphen separated string
* @return The transformed string
* @type {string}
* @since 1.7.0
* @public
* @SecPassthrough {0|return}
*/
jQuery.sap.camelCase = function camelCase(sString) {
return sString.replace( rCamelCase, function( sMatch, sChar ) {
return sChar.toUpperCase();
});
};
var rHyphen = /([A-Z])/g;
/**
* Transforms a camel case string into a hyphen separated string.
*
* @param {string} sString camel case string
* @return The transformed string
* @type {string}
* @since 1.15.0
* @public
* @SecPassthrough {0|return}
*/
jQuery.sap.hyphen = function hyphen(sString) {
return sString.replace( rHyphen, function(sMatch, sChar) {
return "-" + sChar.toLowerCase();
});
};
var rEscapeRegExp = /[-[\]{}()*+?.,\\^$|#\s]/g;
/**
* This function escapes the reserved letters in Regular Expression
* @param {string} sString string to escape
* @return The escaped string
* @type {string}
* @since 1.9.3
* @public
* @SecPassthrough {0|return}
*/
jQuery.sap.escapeRegExp = function escapeRegExp(sString) {
return sString.replace(rEscapeRegExp, "\\$&");
};
/**
* Creates a string from a pattern by replacing placeholders with concrete values.
*
* The syntax of the pattern is inspired by (but not fully equivalent to) the
* java.util.MessageFormat.
*
* Placeholders have the form <code>{ integer }</code>, where any occurrence of
* <code>{0}</code> is replaced by the value with index 0 in <code>aValues</code>,
* <code>{1}</code> y the value with index 1 in <code>aValues</code> etc.
*
* To avoid interpretation of curly braces as placeholders, any non-placeholder fragment
* of the pattern can be enclosed in single quotes. The surrounding single quotes will be
* omitted from the result. Single quotes that are not meant to escape a fragment and
* that should appear in the result, need to be doubled. In the result, only a single
* single quote will occur.
*
* Example Pattern Strings:
* <pre>
* jQuery.sap.formatMessage("Say {0}", ["Hello"]) -> "Say Hello" // normal use case
* jQuery.sap.formatMessage("Say '{0}'", ["Hello"]) -> "Say {0}" // escaped placeholder
* jQuery.sap.formatMessage("Say ''{0}''", ["Hello"]) -> "Say 'Hello'" // doubled single quote
* jQuery.sap.formatMessage("Say '{0}'''", ["Hello"]) -> "Say {0}'" // doubled single quote in quoted fragment
* </pre>
*
* In contrast to java.util.MessageFormat, format types or format styles are not supported.
* Everything after the argument index and up to the first closing curly brace is ignored.
* Nested placeholders (as supported by java.lang.MessageFormat for the format type choice)
* are not ignored but reported as a parse error.
*
* This method throws an Error when the pattern syntax is not fulfilled (e.g. unbalanced curly
* braces, nested placeholders or a non-numerical argument index).
*
* This method can also be used as a formatter within a binding. The first part of a composite binding
* will be used as pattern, the following parts as aValues. If there is only one value and this
* value is an array it will be handled like the default described above.
*
* @param {string} sPattern A pattern string in the described syntax
* @param {any[]} [aValues=[]] The values to be used instead of the placeholders.
*
* @return {string} The formatted result string
* @since 1.12.5
* @SecPassthrough {*|return}
* @public
*/
jQuery.sap.formatMessage = function formatMessage(sPattern, aValues) {
jQuery.sap.assert(typeof sPattern === "string" || sPattern instanceof String, "pattern must be string");
if (arguments.length > 2 || (aValues != null && !jQuery.isArray(aValues))) {
aValues = Array.prototype.slice.call(arguments,1);
}
aValues = aValues || [];
return sPattern.replace(rMessageFormat, function($0,$1,$2,$3,offset) {
if ( $1 ) {
// a doubled single quote in a normal string fragment
// --> emit a single quote
return "'";
} else if ( $2 ) {
// a quoted sequence of chars, potentially containing doubled single quotes again
// --> emit with doubled single quotes replaced by a single quote
return $2.replace(/''/g, "'");
} else if ( $3 ) {
// a welformed curly brace
// --> emit the argument but ignore other parameters
return String(aValues[parseInt($3, 10)]);
}
// e.g. malformed curly braces
// --> throw Error
throw new Error("formatMessage: pattern syntax error at pos. " + offset);
});
};
/**
* Pattern to analyze MessageFormat strings.
*
* Group 1: captures doubled single quotes within the string
* Group 2: captures quoted fragments within the string.
* Note that java.util.MessageFormat silently forgives a missing single quote at
* the end of a pattern. This special case is handled by the RegEx as well.
* Group 3: captures placeholders
* Checks only for numerical argument index, any remainder is ignored up to the next
* closing curly brace. Nested placeholdes are not accepted!
* Group 4: captures any remaining curly braces and indicates syntax errors
*
* [-1] [----- quoted string -----] [------ placeholder ------] [--]
* @private
*/
var rMessageFormat = /('')|'([^']+(?:''[^']*)*)(?:'|$)|\{([0-9]+(?:\s*,[^{}]*)?)\}|[{}]/g;
return jQuery;
}, /* bExport= */ false);
},
"jquery.sap.ui.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// A bridge between the jQuery.sap plugin and the SAPUI5 Core
sap.ui.define(['jquery.sap.global', 'sap/ui/Global'],
function(jQuery, Global) {
"use strict";
//jQuery.sap.require("sap.ui.core.Core"); // cyclic
// !!!!!!!!!!!!!!!!!!!!!!!!!!
// !! EXPERIMENTAL !!
// !!!!!!!!!!!!!!!!!!!!!!!!!!
// FIXME Too tough or just right?
if (!jQuery.sap) {
throw "Initialisation of jQuery.sap.ui failed. jQuery.sap plugin required!";
}
if (!window.sap || !window.sap.ui) {
jQuery.sap.fatal("Initialisation of jQuery.sap.ui failed. Global SAP UI namespace required!");
}
//ensure not to initialize twice
if (jQuery.sap.ui) {
return;
}
// /**
// * Root Namespace for the jQuery UI-Layer plugin provided by SAP SE.
// *
// * @version 1.28.5
// * @namespace
// * @public
// */
// jQuery.sap.ui = {};
function fUIAreaFilter(idx){
return sap.ui.getCore().getUIArea(this.id) != null;
}
function fgetUIArea(idx, odomref){
return sap.ui.getCore().getUIArea(this.id);
}
function fgetUIAreaOfCtrl(oCtrl, idx){
return oCtrl.getUIArea().getInterface();
}
/**
* @param {object} oRootControl
* @name jQuery#root
* @function
* @public
*/
jQuery.fn.root = function(oRootControl) {
// handle 'setRoot'
if (oRootControl) {
sap.ui.getCore().setRoot(this.get(0), oRootControl);
return this;
}
// and 'getRoot' behavior.
var aControls = this.control();
if (aControls.length > 0) {
return jQuery.map(aControls, fgetUIAreaOfCtrl);
}
var aUIAreas = this.uiarea();
if (aUIAreas.length > 0) {
// we have UIAreas
return aUIAreas;
}
// create UIAreas
this.each(function(idx){
sap.ui.getCore().createUIArea(this);
});
return this;
};
/**
* @param {int} iIdx
* @name jQuery#uiarea
* @function
* @public
*/
jQuery.fn.uiarea = function(iIdx) {
// UIAreas need to have IDs... so reduce to those elements first
var aUIAreas = this.slice("[id]").filter(fUIAreaFilter).map(fgetUIArea).get();
return typeof (iIdx) === "number" ? aUIAreas[iIdx] : aUIAreas;
};
/**
* Function identifying the closest SAPUI5 Control in the given jQuery context (provided via jQuery.map method).
* @private
*/
function fgetControl() {
// as with jQuery 1.4.3 and 1.4.4 there is a big problem here, we increase checks here
if (!this || !this.nodeType || this.nodeType === 9) {
return null;
} // in this case, we are on the HTML Document and cannot do anything
// in IE8 'closest' might fail (e.g. when the element is not in the current document tree)
// The following line would probably also work for the 'try-catch' below but induce performance penalty in EVERY call of this method.
// if(jQuery(document.documentElement).has(this)) {
try {
var sId = jQuery(this).closest("[data-sap-ui]").attr("id");
return sId ? sap.ui.getCore().byId(sId) : null;
} catch (e) {
// probably IE8 case where element is not in current document tree... ==> there is no current control
return null;
}
}
/**
* Extension function to the jQuery.fn which identifies SAPUI5 controls in the given jQuery context.
*
* @param {int} [idx] optional parameter to return the control instance at the given idx's position in the array.
* @returns {sap.ui.core.Control[] | sap.ui.core.Control | null} depending on the given context and idx parameter an array of controls, an instance or null.
* @name jQuery#control
* @function
* @public
*/
jQuery.fn.control = function(idx) {
var aControls = this.map(fgetControl);
if (idx === undefined || isNaN(idx)) {
return aControls.get();
} else {
return aControls.get(idx);
}
};
/**
* EXPERIMENTAL!!
* Creates a new control of the given type and places it into the first DOM object of the jQuery collection.
* The type string is case sensitive.
*
* @param {string} sControlType the control type (fully qualified, like "sap.ui.dev.GoogleMap"; if no package is given, the package "sap.ui.commons" is assumed)
* @param {string} [sId] optional id for the new control; generated automatically if no non-empty id is given
* @param {object} [oConfiguration] optional map/JSON-object with initial values for the new control
* @returns {jQuery} the given jQuery object
* @private
*/
jQuery.fn.sapui = function(sControlType, sId, oConfiguration) {
return this.each(function() { // TODO: hack for Steffen; (point is not clear, as this adds identical controls to many DOM elements...); remove soon
var oControl = null;
if (this) {
// allow omitting the package prefix because this looks less Java-like... sap.ui.commons is the default package
if (sControlType.indexOf(".") == -1) {
sControlType = "sap.ui.commons." + sControlType;
}
// instantiate the control
var fnClass = jQuery.sap.getObject(sControlType);
if (fnClass) {
// TODO: hack for Steffen; remove later
if (typeof oConfiguration == 'object' && typeof oConfiguration.press == 'function') {
oConfiguration.press = jQuery.proxy(oConfiguration.press,this);
}
oControl = new (fnClass)(sId, oConfiguration); // sId might actually contain oConfiguration, the Element constructor will take care of this
// placeAt first DomRef in collection
oControl.placeAt(this);
// TODO: avoid the direct call to applyChanges() in favor of a delayed version that potentially bundles several changes
//sap.ui.getCore().applyChanges();
}
}
});
};
return jQuery;
}, /* bExport= */ false);
},
"sap/ui/Global.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/**
* @overview Initialization for the SAP UI Library
*
* This module creates the main SAP namespaces {@link sap} and automatically
* registers it to the OpenAjax hub if that exists.
*
* This class provides method {@link #namespace} to register namespaces to the
* SAP UI Library.
*
* @sample
* Ensures a control can be used afterwards but does not load immediately
* sap.ui.lazyRequire("sap.ui.core.Control");
* sap.ui.lazyRequire("sap.ui.commons.Button");
*
* @version 1.28.5
* @author Martin Schaus, Daniel Brinkmann
* @public
*/
/*global OpenAjax */// declare unusual global vars for JSLint/SAPUI5 validation
// Register to the OpenAjax Hub if it exists
sap.ui.define(['jquery.sap.global', 'jquery.sap.dom'],
function(jQuery/* , jQuerySap */) {
"use strict";
if (window.OpenAjax && window.OpenAjax.hub) {
OpenAjax.hub.registerLibrary("sap", "http://www.sap.com/", "0.1", {});
}
/**
* Root namespace for JavaScript functionality provided by SAP SE.
*
* The <code>sap</code> namespace is automatically registered with the
* OpenAjax hub if it exists.
*
* @version 1.28.5
* @namespace
* @public
* @name sap
*/
if ( typeof window.sap !== "object" && typeof window.sap !== "function" ) {
window.sap = {};
}
/**
* The <code>sap.ui</code> namespace is the central OpenAjax compliant entry
* point for UI related JavaScript functionality provided by SAP.
*
* @version 1.28.5
* @namespace
* @name sap.ui
* @public
*/
if ( typeof window.sap.ui !== "object") {
window.sap.ui = {};
}
sap.ui = jQuery.extend(sap.ui, {
/**
* The version of the SAP UI Library
* @type string
*/
version: "1.28.5",
buildinfo : { lastchange : "", buildtime : "20150427-1201" }
});
/**
* Loads the version info file (resources/sap-ui-version.json) and returns
* it or if a library name is specified then the version info of the individual
* library will be returned.
*
* In case of the version info file is not available an error will occur when
* calling this function.
*
* @param {string} [sLibName] name of the library (e.g. "sap.ui.core")
* @return {object} either the full version info or the library specific one
* @public
* @static
*/
sap.ui.getVersionInfo = function(sLibName) {
if (!sap.ui.versioninfo) {
sap.ui.versioninfo = jQuery.sap.loadResource("sap-ui-version.json");
}
if (sLibName !== undefined) {
// find the version of the individual library
var aLibs = sap.ui.versioninfo.libraries;
for (var i = 0, l = aLibs.length; i < l; i++) {
if (aLibs[i].name === sLibName) {
return aLibs[i];
}
}
} else {
// returns the full version info
return sap.ui.versioninfo;
}
};
/**
* Ensures that a given a namespace or hierarchy of nested namespaces exists in the
* current <code>window</code>.
*
* @param {string} sNamespace
* @return {object} the innermost namespace of the hierarchy
* @public
* @static
* @deprecated Use jQuery.sap.declare or jQuery.sap.getObject(...,0) instead
*/
sap.ui.namespace = function(sNamespace){
jQuery.sap.assert(false, "sap.ui.namespace is long time deprecated and shouldn't be used");
return jQuery.sap.getObject(sNamespace, 0);
};
/**
* Creates a lazy loading stub for a given class <code>sClassName</code>.
*
* If the class has been loaded already, nothing is done. Otherwise a stub object
* or constructor and - optionally - a set of stub methods are created.
* All created stubs will load the corresponding module on execution
* and then delegate to their counterpart in the loaded module.
*
* When no methods are given or when the list of methods contains the special name
* "new" (which is an operator can't be used as method name in JavaScript), then a
* stub <b>constructor</b> for class <code>sClassName</code> is created.
* Otherwise, a plain object is created.
*
* <b>Note</b>: Accessing any stub as a plain object without executing it (no matter
* whether it is a function or an object) won't load the module and therefore most like
* won't work as expected. This is a fundamental restriction of the lazy loader approach.
* It could only be fixed with JavaScript 1.5 features that are not available in all
* UI5 target browsers (e.g. not in IE8).
*
* <b>Note</b>: As a side effect of this method, the namespace containing the given
* class is created <b>immediately</b>.
*
* @param {string} sClassName Fully qualified name (dot notation) of the class that should be prepared
* @param {string} [sMethods='new'] space separated list of additional (static) methods that should be created as stubs
* @param {string} [sModuleName] name of the module to load, defaults to the class name
* @public
* @static
*/
sap.ui.lazyRequire = function(sClassName, sMethods, sModuleName) {
jQuery.sap.assert(typeof sClassName === "string" && sClassName, "lazyRequire: sClassName must be a non-empty string");
jQuery.sap.assert(!sMethods || typeof sMethods === "string", "lazyRequire: sMethods must be empty or a string");
var sFullClass = sClassName.replace(/\//gi,"\."),
iLastDotPos = sFullClass.lastIndexOf("."),
sPackage = sFullClass.substr(0, iLastDotPos),
sClass = sFullClass.substr(iLastDotPos + 1),
oPackage = jQuery.sap.getObject(sPackage, 0),
oClass = oPackage[sClass],
aMethods = (sMethods || "new").split(" "),
iConstructor = jQuery.inArray("new", aMethods);
sModuleName = sModuleName || sFullClass;
if (!oClass) {
if ( iConstructor >= 0 ) {
// Create dummy constructor which loads the class on demand
oClass = function() {
jQuery.sap.log.debug("lazy stub for '" + sFullClass + "' (constructor) called.");
jQuery.sap.require(sModuleName);
var oRealClass = oPackage[sClass];
jQuery.sap.assert(typeof oRealClass === "function", "lazyRequire: oRealClass must be a function after loading");
if ( oRealClass._sapUiLazyLoader ) {
throw new Error("lazyRequire: stub '" + sFullClass + "'has not been replaced by module '" + sModuleName + "'");
}
// create a new instance and invoke the constructor
var oInstance = jQuery.sap.newObject(oRealClass.prototype);
var oResult = oRealClass.apply(oInstance, arguments);
if (oResult && (typeof oResult === "function" || typeof oResult === "object")) {
oInstance = oResult;
}
return oInstance;
};
// mark the stub as lazy loader
oClass._sapUiLazyLoader = true;
aMethods.splice(iConstructor,1);
} else {
// Create dummy object
oClass = {};
}
// remember the stub
oPackage[sClass] = oClass;
}
// add stub methods to it
jQuery.each(aMethods, function (i,sMethod) {
// check whether method is already available
if (!oClass[sMethod]) {
oClass[sMethod] = function() {
jQuery.sap.log.debug("lazy stub for '" + sFullClass + "." + sMethod + "' called.");
jQuery.sap.require(sModuleName);
var oRealClass = oPackage[sClass];
jQuery.sap.assert(typeof oRealClass === "function" || typeof oRealClass === "object", "lazyRequire: oRealClass must be a function or object after loading");
jQuery.sap.assert(typeof oRealClass[sMethod] === "function", "lazyRequire: method must be a function");
if (oRealClass[sMethod]._sapUiLazyLoader ) {
throw new Error("lazyRequire: stub '" + sFullClass + "." + sMethod + "' has not been replaced by loaded module '" + sModuleName + "'");
}
return oRealClass[sMethod].apply(oRealClass, arguments);
};
oClass[sMethod]._sapUiLazyLoader = true;
}
});
};
/**
* Returns the URL of a resource that belongs to the given library and has the given relative location within the library.
* This is mainly meant for static resources like images that are inside the library.
* It is NOT meant for access to JavaScript modules or anything for which a different URL has been registered with jQuery.sap.registerModulePath(). For
* these cases use jQuery.sap.getModulePath().
* It DOES work, however, when the given sResourcePath starts with "themes/" (= when it is a theme-dependent resource). Even when for this theme a different
* location outside the normal library location is configured.
*
* @param {string} sLibraryName the name of a library, like "sap.ui.commons"
* @param {string} sResourcePath the relative path of a resource inside this library, like "img/mypic.png" or "themes/my_theme/img/mypic.png"
* @returns {string} the URL of the requested resource
*
* @static
* @public
*/
sap.ui.resource = function(sLibraryName, sResourcePath) {
jQuery.sap.assert(typeof sLibraryName === "string", "sLibraryName must be a string");
jQuery.sap.assert(typeof sResourcePath === "string", "sResourcePath must be a string");
// special handling for theme-dependent resources: move theme folder into module name
var match = sResourcePath.match(/^themes\/([^\/]+)\//);
if (match) {
sLibraryName += ".themes." + match[1];
sResourcePath = sResourcePath.substr(match[0].length);
}
return jQuery.sap.getModulePath(sLibraryName, '/') + sResourcePath;
};
/**
* Redirects access to resources that are part of the given namespace to a location
* relative to the assumed <b>application root folder</b>.
*
* Any UI5 managed resource (view, controller, control, JavaScript module, CSS file, etc.)
* whose resource name starts with <code>sNamespace</code>, will be loaded from an
* equally named subfolder of the <b>application root folder</b>.
* If the resource name consists of multiple segments (separated by a dot), each segment
* is assumed to represent an individual folder. In other words: when a resource name is
* converted to an URL, any dots ('.') are converted to slashes ('/').
*
* <b>Limitation:</b> For the time being, the <b>application root folder</b> is assumed to be
* the same as the folder where the current page resides in.
*
* Usage sample:
* <pre>
* // Let UI5 know that resources, whose name starts with "com.mycompany.myapp"
* // should be loaded from the URL location "./com/mycompany/myapp"
* sap.ui.localResources("com.mycompany.myapp");
*
* // The following call implicitly will use the mapping done by the previous line
* // It will load a view from ./com/mycompany/myapp/views/Main.view.xml
* sap.ui.view({ view : "com.mycompany.myapp.views.Main", type : sap.ui.core.mvc.ViewType.XML});
* </pre>
*
* When applications need a more flexible mapping between resource names and their location,
* they can use {@link jQuery.sap.registerModulePath}.
*
* It is intended to make this configuration obsolete in future releases, but for the time
* being, applications must call this method when they want to store resources relative to
* the assumed application root folder.
*
* @param {string} sNamespace Namespace prefix for which to load resources relative to the application root folder
* @public
* @static
* @see jQuery.sap.registerModulePath
*/
sap.ui.localResources = function(sNamespace) {
jQuery.sap.assert(sNamespace, "sNamespace must not be empty");
jQuery.sap.registerModulePath(sNamespace, "./" + sNamespace.replace(/\./g, "/"));
};
return sap.ui;
}, /* bExport= */ true);
},
"sap/ui/base/BindingParser.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides static class sap.ui.base.BindingParser
sap.ui.define(['jquery.sap.global', './ExpressionParser', 'jquery.sap.script'],
function(jQuery, ExpressionParser/* , jQuerySap */) {
"use strict";
/**
* Regular expression to check for a (new) object literal
*/
var rObject = /^\{\s*[a-zA-Z_][a-zA-Z0-9_]*\s*:/;
/**
* Regular expression to split the binding string into hard coded string fragments and embedded bindings.
*
* Also handles escaping of '{' and '}'.
*/
var rFragments = /(\\[\\\{\}])|(\{)/g;
/**
* Regular expression to escape potential binding chars
*/
var rBindingChars = /([\\\{\}])/g;
/**
* Helper to create a formatter function. Only used to reduce the closure size of the formatter
*/
function makeFormatter(aFragments) {
var fnFormatter = function(aValues) {
var aResult = [],
l = aFragments.length,
i;
for (i = 0; i < l; i++) {
if ( typeof aFragments[i] === "number" ) {
// a numerical fragment references the part with the same number
aResult.push(arguments[aFragments[i]]);
} else {
// anything else is a string fragment
aResult.push(aFragments[i]);
}
}
return aResult.join('');
};
fnFormatter.textFragments = aFragments;
return fnFormatter;
}
/**
* Creates a binding info object with the given path.
*
* If the path contains a model specifier (prefix separated with a '>'),
* the <code>model</code> property is set as well and the prefix is
* removed from the path.
*/
function makeSimpleBindingInfo(sPath) {
var iPos = sPath.indexOf(">"),
oBindingInfo = { path : sPath };
if ( iPos > 0 ) {
oBindingInfo.model = sPath.slice(0,iPos);
oBindingInfo.path = sPath.slice(iPos + 1);
}
return oBindingInfo;
}
/**
* @static
* @namespace
* @alias sap.ui.base.BindingParser
*/
var BindingParser = {};
BindingParser._keepBindingStrings = false;
BindingParser.simpleParser = function(sString, oContext) {
if ( jQuery.sap.startsWith(sString, "{") && jQuery.sap.endsWith(sString, "}") ) {
return makeSimpleBindingInfo(sString.slice(1, -1));
}
};
BindingParser.simpleParser.escape = function(sValue) {
// there was no escaping defined for the simple parser
return sValue;
};
BindingParser.complexParser = function(sString, oContext, bUnescape) {
var parseObject = jQuery.sap.parseJS,
oBindingInfo = {parts:[]},
aFragments = [],
bUnescaped,
p = 0,
m,
oEmbeddedBinding,
bContainsExpression;
function resolveRef(o,sProp) {
if ( typeof o[sProp] === "string" ) {
var sName = o[sProp];
if ( jQuery.sap.startsWith(o[sProp], ".") ) {
o[sProp] = jQuery.proxy(jQuery.sap.getObject(o[sProp].slice(1), undefined, oContext), oContext);
} else {
o[sProp] = jQuery.sap.getObject(o[sProp]);
}
if (typeof (o[sProp]) !== "function") {
jQuery.sap.log.error(sProp + " function " + sName + " not found!");
}
}
}
function resolveType(o,sProp) {
var FNType;
if ( typeof o[sProp] === "string" ) {
if ( jQuery.sap.startsWith(o[sProp], ".") ) {
FNType = jQuery.sap.getObject(o[sProp].slice(1), undefined, oContext);
} else {
FNType = jQuery.sap.getObject(o[sProp]);
}
// TODO find another solution for the type parameters?
if (typeof FNType === "function") {
o[sProp] = new FNType(o.formatOptions, o.constraints);
} else {
o[sProp] = FNType;
}
delete o.formatOptions;
delete o.constraints;
}
}
function resolveEvents(o,sProp) {
if (!(jQuery.isPlainObject(o[sProp]))) {
return;
}
jQuery.each(o[sProp], function(sName, oObject) {
resolveRef(o[sProp], sName);
});
}
function resolveObject(o,sProp, sParentProp) {
var FNType;
if (!(typeof o[sProp] === "object" || jQuery.isArray(o[sProp]))) {
return;
}
if (jQuery.isArray(o[sProp])) {
jQuery.each(o[sProp], function(iIndex, oObject) {
resolveObject(o[sProp], iIndex, sProp);
});
} else {
if (sProp === "filters" || sParentProp === "filters") {
FNType = jQuery.sap.getObject("sap.ui.model.Filter");
} else if (sProp === "sorter" || sParentProp === "sorter") {
FNType = jQuery.sap.getObject("sap.ui.model.Sorter");
resolveRef(o[sProp], "group");
}
if (FNType) {
o[sProp] = new FNType(o[sProp]);
}
}
}
/**
* Determines the binding info for the given string sInput starting at the given iStart and
* returns an object with the corresponding binding info as <code>result</code> and the
* position where to continue parsing as <code>at</code> property.
*
* @param {string} sInput The input string from which to resolve an embedded binding
* @param {number} iStart The start index for binding resolution in the input string
* @returns {object} An object with the following properties:
* result: The binding info for the embedded binding
* at: The position after the last character for the embedded binding in the input string
*/
function resolveEmbeddedBinding(sInput, iStart) {
var oParseResult,
iEnd;
// an embedded binding: check for a property name that would indicate a complex object
if ( rObject.test(sInput.slice(iStart)) ) {
oParseResult = parseObject(sInput, iStart);
resolveType(oParseResult.result,'type');
resolveObject(oParseResult.result,'filters');
resolveObject(oParseResult.result,'sorter');
resolveEvents(oParseResult.result,'events');
resolveRef(oParseResult.result,'formatter');
resolveRef(oParseResult.result,'factory'); // list binding
resolveRef(oParseResult.result,'groupHeaderFactory');
return oParseResult;
}
// otherwise it must be a simple binding (path only)
// TODO find closing brace via regex as well?
iEnd = sInput.indexOf('}', iStart);
if ( iEnd < iStart ) {
throw new SyntaxError("no closing braces found in '" + sInput + "' after pos:" + iStart);
}
return {
result: makeSimpleBindingInfo(sInput.slice(iStart + 1, iEnd)),
at: iEnd + 1
};
}
rFragments.lastIndex = 0; //previous parse call may have thrown an Error: reset lastIndex
while ( (m = rFragments.exec(sString)) !== null ) {
// check for a skipped literal string fragment
if ( p < m.index ) {
aFragments.push(sString.slice(p, m.index));
}
// handle the different kinds of matches
if ( m[1] ) {
// an escaped opening bracket, closing bracket or backslash
aFragments.push(m[1].slice(1));
bUnescaped = true;
} else {
aFragments.push(oBindingInfo.parts.length);
if (sString.charAt(m.index + 1) === "=") { //expression
oEmbeddedBinding = ExpressionParser.parse(resolveEmbeddedBinding, sString,
m.index + 2);
if (sString.charAt(oEmbeddedBinding.at) !== "}") {
throw new SyntaxError("Expected '}' and instead saw '"
+ sString.charAt(oEmbeddedBinding.at)
+ "' in expression binding "
+ sString
+ " at position "
+ oEmbeddedBinding.at);
}
oEmbeddedBinding.at += 1;
if (oEmbeddedBinding.result) {
bContainsExpression = true;
} else {
aFragments[aFragments.length - 1] = String(oEmbeddedBinding.constant);
bUnescaped = true;
}
} else {
oEmbeddedBinding = resolveEmbeddedBinding(sString, m.index);
}
if (oEmbeddedBinding.result) {
oBindingInfo.parts.push(oEmbeddedBinding.result);
}
rFragments.lastIndex = oEmbeddedBinding.at;
}
// remember where we are
p = rFragments.lastIndex;
}
// check for a trailing literal string fragment
if ( p < sString.length ) {
aFragments.push(sString.slice(p));
}
// only if a part has been found we can return a binding info
if (oBindingInfo.parts.length > 0) {
if ( aFragments.length === 1 /* implies: && typeof aFragments[0] === "number" */ ) {
// special case: a single binding only
oBindingInfo = oBindingInfo.parts[0];
} else /* if ( aFragments.length > 1 ) */ {
if (bContainsExpression) {
throw new SyntaxError("Expression not allowed in composite binding: "
+ sString);
}
// create the formatter function from the fragments
oBindingInfo.formatter = makeFormatter(aFragments);
}
if (BindingParser._keepBindingStrings) {
oBindingInfo.bindingString = sString;
}
return oBindingInfo;
} else if ( bUnescape && bUnescaped ) {
return aFragments.join('');
}
};
BindingParser.complexParser.escape = function(sValue) {
return sValue.replace(rBindingChars, "\\$1");
};
return BindingParser;
}, /* bExport= */ true);
},
"sap/ui/base/DataType.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.DataType
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* @class Describes the metadata of a data type and provides methods for validation.
* @author Frank Weigel
* @since 0.9.0
* @alias sap.ui.base.DataType
*/
var DataType = function() {
// Avoid construction of a DataType.
// DataType is only a function to support the "instanceof" operator.
throw new Error();
};
/**
* The qualified name of the data type.
* Note that this name usually equals the design time name of the type.
* Only for primitive types it differs, namely it omits the package 'sap.ui.core'.
* @return {string} name of the data type
* @public
*/
DataType.prototype.getName = function() {
return undefined;
};
/**
* The base type of this type or undefined if this is a primitive type.
* @return {sap.ui.base.DataType} base type or undefined
* @public
*/
DataType.prototype.getBaseType = function() {
return undefined;
};
/**
* The primitive base type of this type or the primitive type itself.
* @return {sap.ui.base.DataType} the primitive type
* @public
*/
DataType.prototype.getPrimitiveType = function() {
/*eslint-disable consistent-this*/
var oType = this;
/*eslint-enable consistent-this*/
while (oType.getBaseType()) {
oType = oType.getBaseType();
}
return oType;
};
/**
* The component type of this type or undefined if this is not an array.
* @return {sap.ui.base.DataType} component type or undefined
* @public
*/
DataType.prototype.getComponentType = function() {
return undefined;
};
/**
* The default value for this type. Each type must define a default value.
* @return {any} default value of the data type. The type of the returned value
* must match the JavaScript type of the data type (a string for string types etc.)
* @public
*/
DataType.prototype.getDefaultValue = function() {
return undefined;
};
/**
* Whether this type is an array type.
* @return {boolean} whether this type is an array type
* @public
*/
DataType.prototype.isArrayType = function() {
return undefined;
};
/**
* Parses the given string value and converts it into the specific data type.
* @param {string} sValue string representation for a value of this type
* @return the value in the correct internal format
* @public
*/
DataType.prototype.parseValue = function(sValue) {
// currently this function considers to handle primitive values
// - in future may be other values might be also relevant.
var sType = this.getName();
if (sType == "string") {
return sValue;
} else if (sType == "boolean") {
return sValue == "true";
} else if (sType == "int") {
return parseInt(sValue, 10);
} else if (sType == "float") {
return parseFloat(sValue);
} else if (sType == "object") {
return sValue ? jQuery.parseJSON(sValue) : null;
} else {
// support for other types like e.g.
// sap.ui.core.CSSSize (just apply)
return sValue;
}
};
/**
* A validation check. To be implemented by concrete types.
* @param {any} vValue the value to be checked
* @return {boolean} whether the given value is valid for this data type (without conversion)
* @public
*/
DataType.prototype.isValid = undefined;
/**
* Sets the normalizer function for that data type
*
* @param {function} fnNormalizer the function to call for normalizing. Will be called with the value
* as the first parameter. It must return the (normalized) value.
* @public
*/
DataType.prototype.setNormalizer = function(fnNormalizer) {
jQuery.sap.assert(typeof fnNormalizer === "function", "DataType.setNormalizer: fnNormalizer must be a function");
this._fnNormalizer = fnNormalizer;
};
/**
* Changes a value using the normalizer specified for this datatype
*
* @param {object} oValue the value to be normalized
* @return the normalized value
* @public
*/
DataType.prototype.normalize = function(oValue) {
if (typeof this._fnNormalizer === "function") {
return this._fnNormalizer(oValue);
} else {
return oValue;
}
};
(function() {
function createType(name, s, base) {
jQuery.sap.assert(typeof name === "string" && !!name, "DataType.<createType>: type name must be a string");
jQuery.sap.assert(!base || base instanceof DataType, "DataType.<createType>: base type must be empty or a DataType");
s = s || {};
// create a new type object with the base type as prototype
var baseObject = base || DataType.prototype;
var type = jQuery.sap.newObject(baseObject);
// getter for the name
type.getName = function() {
return name;
};
// if a default value is specified, create a getter for it
if ( s.hasOwnProperty("defaultValue") ) {
var vDefault = s.defaultValue;
type.getDefaultValue = function() {
return vDefault;
};
}
// if a validator is specified either chain it with the base type validator
// or set it if no base validator exists
if ( s.hasOwnProperty("isValid") ) {
var fnIsValid = s.isValid;
type.isValid = baseObject.isValid ? function(vValue) {
if ( !baseObject.isValid(vValue) ) {
return false;
}
return fnIsValid(vValue);
} : fnIsValid;
}
// not an array type
type.isArrayType = function() {
return false;
};
// return the base type
type.getBaseType = function() {
return base;
};
return type;
}
function createArrayType(componentType) {
jQuery.sap.assert(componentType instanceof DataType, "DataType.<createArrayType>: componentType must be a DataType");
// create a new type object with the base type as prototype
var type = jQuery.sap.newObject(DataType.prototype);
// getter for the name
type.getName = function() {
return componentType.getName() + "[]";
};
// getter for component type
type.getComponentType = function() {
return componentType;
};
// array validator
type.isValid = function(aValues) {
if (aValues === null) {
return true;
}
if (jQuery.isArray(aValues)) {
for (var i = 0; i < aValues.length; i++) {
if (!componentType.isValid(aValues[i])) {
return false;
}
}
return true;
}
return false;
};
// array parser
type.parseValue = function(sValue) {
var aValues = sValue.split(",");
for (var i = 0; i < aValues.length; i++) {
aValues[i] = componentType.parseValue(aValues[i]);
}
return aValues;
};
// is an array type
type.isArrayType = function() {
return true;
};
// return the base type
type.getBaseType = function() {
return ARRAY_TYPE;
};
return type;
}
var PREDEFINED_TYPES = {
"any" :
createType("any", {
defaultValue : null,
isValid : function(vValue) {
return true;
}
}),
"boolean" :
createType("boolean", {
defaultValue : false,
isValid : function(vValue) {
return typeof vValue === "boolean";
}
}),
"int" :
createType("int", {
defaultValue : 0,
isValid : function(vValue) {
return typeof vValue === "number" && Math.floor(vValue) == vValue;
}
}),
"float" :
createType("float", {
defaultValue : 0.0,
isValid : function(vValue) {
return typeof vValue === "number";
}
}),
"string" :
createType("string", {
defaultValue : "",
isValid : function(vValue) {
return typeof vValue === "string" || vValue instanceof String;
}
}),
"object" :
createType("object", {
defaultValue : null,
isValid : function(vValue) {
return typeof vValue === "object" || typeof vValue === "function";
}
})
};
// Array type is not part of predefined types to avoid direct usage as property type
var ARRAY_TYPE = createType("array", {
defaultValue : [],
isValid : function(vValue) {
return jQuery.isArray(vValue);
}
});
/**
* Returns the type object for the type with the given name.
*
* @param {string} sTypeName name of the type to be retrieved
* @return the type object or undefined when no such type object exists.
* @public
*/
DataType.getType = function(sTypeName) {
if (sTypeName.indexOf("[]") > 0) {
var sComponentTypeName = sTypeName.substr(0, sTypeName.length - 2),
oComponentType = this.getType(sComponentTypeName);
return oComponentType && createArrayType(oComponentType);
} else {
return PREDEFINED_TYPES[sTypeName] || jQuery.sap.getObject(sTypeName);
}
};
/**
* Creates a new type as a subtype of a given type.
* @param {string} sName the unique name of the new type
* @param {object} [mSettings settings] for the new type
* @param {any} [mSettings.defaultValue] the default value for the new type
* @param {function} [mSettings.isValid] a validator function for values of the new type
* @param {sap.ui.base.DataType} [base] the base type for the new type
* @public
*/
DataType.createType = createType;
// ---- minimal support for interface types ----
var mInterfaces = {};
/**
* Registers the given array of type names as known interface types.
* Only purpose is to enable the {@link #isInterfaceType} check.
* @param {string[]} aTypes interface types to be reigstered
* @private
*/
DataType.registerInterfaceTypes = function(aTypes) {
for (var i = 0; i < aTypes.length; i++) {
jQuery.sap.setObject(aTypes[i], mInterfaces[aTypes[i]] = new String(aTypes[i]));
}
};
/**
* @param {string} sType name of type to check
* @return {boolean} whether the given type is known to be an interface type
* @private
*/
DataType.isInterfaceType = function(sType) {
return mInterfaces.hasOwnProperty(sType) && jQuery.sap.getObject(sType) === mInterfaces[sType];
};
}());
return DataType;
}, /* bExport= */ true);
},
"sap/ui/base/Event.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.Event
sap.ui.define(['jquery.sap.global', './Object'],
function(jQuery, BaseObject) {
"use strict";
/**
* Creates an event with the given sId, linked to the provided oSource and enriched with the mParameters.
*
* @param {string} sId The id of the event
* @param {sap.ui.base.EventProvider} oSource The source of the event
* @param {object} mParameters A map of parameters for this event
*
* @class An Event object consisting of an id, a source and a map of parameters
* @extends sap.ui.base.Object
* @implements sap.ui.base.Poolable
* @author Malte Wedel, Daniel Brinkmann
* @version 1.28.5
* @alias sap.ui.base.Event
* @public
*/
var Event = BaseObject.extend("sap.ui.base.Event", /** @lends sap.ui.base.Event.prototype */ {
constructor : function(sId, oSource, mParameters) {
BaseObject.apply(this);
if (arguments.length > 0) {
this.init(sId, oSource, mParameters);
}
}
});
/**
* Init this event with its data.
*
* The <code>init</code> method is called by an object pool when the
* object is (re-)activated for a new caller.
*
* When no <code>mParameters</code> are given, an empty object is used instead.
*
* @param {string} sId The id of the event
* @param {sap.ui.base.EventProvider} oSource The source of the event
* @param {object} [mParameters] the event parameters
*
* @see sap.ui.base.Poolable.prototype#init
*/
Event.prototype.init = function(sId, oSource, mParameters) {
jQuery.sap.assert(typeof sId === "string", "Event.init: sId must be a string");
jQuery.sap.assert(oSource instanceof sap.ui.base.EventProvider, "Event.init: oSource must be an EventProvider");
this.sId = sId;
this.oSource = oSource;
this.mParameters = mParameters || {};
this.bCancelBubble = false;
this.bPreventDefault = false;
};
/**
* Reset event data, needed for pooling
* @see sap.ui.base.Poolable.prototype#reset
*/
Event.prototype.reset = function() {
this.sId = "";
this.oSource = null;
this.mParameters = null;
this.bCancelBubble = false;
this.bPreventDefault = false;
};
/**
* Returns the id of the event
* @return {string} The id of the event
* @public
*/
Event.prototype.getId = function() {
return this.sId;
};
/**
* Returns the source of the event
* @return {sap.ui.base.EventProvider} The source of the event
* @public
*/
Event.prototype.getSource = function() {
return this.oSource;
};
/**
* Returns all parameter values of the event keyed by their names.
* @return {map} All parameters of the event keyed by name
* @public
*/
Event.prototype.getParameters = function() {
return this.mParameters;
};
/**
* Returns the value of the parameter with the given sName.
*
* @param {string} sName the name of the parameter to return
* @return {any} the value for the named parameter
* @public
*/
Event.prototype.getParameter = function(sName) {
jQuery.sap.assert(typeof sName === "string" && sName, "Event.getParameter: sName must be a non-empty string");
return this.mParameters[sName];
};
/**
* Cancel bubbling of the event.
* @public
*/
Event.prototype.cancelBubble = function() {
this.bCancelBubble = true;
};
/**
* Prevent the default action of this event.
* @public
*/
Event.prototype.preventDefault = function() {
this.bPreventDefault = true;
};
return Event;
}, /* bExport= */ true);
},
"sap/ui/base/EventProvider.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.EventProvider
sap.ui.define(['jquery.sap.global', './Event', './Object', './ObjectPool'],
function(jQuery, Event, BaseObject, ObjectPool) {
"use strict";
/**
* Creates an instance of EventProvider.
* @class Provides internal eventing facilities for objects, so other objects can attach
* and detach events, and are notified, when events are fired
*
* @abstract
* @extends sap.ui.base.Object
* @author Malte Wedel, Daniel Brinkmann
* @version 1.28.5
* @constructor
* @public
* @alias sap.ui.base.EventProvider
*/
var EventProvider = BaseObject.extend("sap.ui.base.EventProvider", /* @lends sap.ui.base.EventProvider */ {
constructor : function() {
BaseObject.call(this);
/**
* A map of arrays of event registrations keyed by the event names
* @private
*/
this.mEventRegistry = {};
}
});
/**
* Map of event names and ids, that are provided by this class
* @private
* @static
*/
EventProvider.M_EVENTS = {EventHandlerChange:"EventHandlerChange"};
/**
* Pool is defined on the prototype to be shared among all EventProviders
* @private
*/
EventProvider.prototype.oEventPool = new ObjectPool(Event);
/**
* Adds an event registration for the given object and given event name
*
* @param {string}
* sEventId The identifier of the event to listen for
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or on the event provider-instance
* @param {object}
* [oListener] The object, that wants to be notified, when the event occurs
* @return {sap.ui.base.EventProvider} Returns <code>this</code> to allow method chaining
* @public
*/
EventProvider.prototype.attachEvent = function(sEventId, oData, fnFunction, oListener) {
jQuery.sap.assert(typeof (sEventId) === "string" && sEventId, "EventProvider.attachEvent: sEventId must be a non-empty string");
if (typeof (oData) === "function") {
//one could also increase the check in the line above
//if(typeof(oData) === "function" && oListener === undefined) {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
jQuery.sap.assert(typeof (fnFunction) === "function", "EventProvider.attachEvent: fnFunction must be a function");
jQuery.sap.assert(!oListener || typeof (oListener) === "object", "EventProvider.attachEvent: oListener must be empty or an object");
if (!this.mEventRegistry[sEventId]) {
this.mEventRegistry[sEventId] = [];
}
this.mEventRegistry[sEventId].push({oListener:oListener, fFunction:fnFunction, oData: oData});
// Inform interested parties about changed EventHandlers
this.fireEvent(EventProvider.M_EVENTS.EventHandlerChange, {EventId: sEventId, type: 'listenerAttached'});
return this;
};
/**
* Adds a one time event registration for the given object and given event name. When the event occurs, the handler function is called and removed
* from registration.
*
* @param {string}
* sEventId The identifier of the event to listen for
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or on the event provider-instance
* @param {object}
* [oListener] The object, that wants to be notified, when the event occurs
* @return {sap.ui.base.EventProvider} Returns <code>this</code> to allow method chaining
* @public
*/
EventProvider.prototype.attachEventOnce = function(sEventId, oData, fnFunction, oListener) {
if (typeof (oData) === "function") {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
function fnOnce() {
this.detachEvent(sEventId, fnOnce); // this is always the control, due to the context undefined in the attach call below
fnFunction.apply(oListener || this, arguments); // needs to do the same resolution as in fireEvent
}
this.attachEvent(sEventId, oData, fnOnce, undefined); // a listener of undefined enforce a context of this even after clone
return this;
};
/**
* Removes an event registration for the given object and given event name.
*
* The passed parameters must match those used for registration with {@link #attachEvent } beforehand!
*
* @param {string}
* sEventId The identifier of the event to detach from
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* [oListener] The object, that wants to be notified, when the event occurs
* @return {sap.ui.base.EventProvider} Returns <code>this</code> to allow method chaining
* @public
*/
EventProvider.prototype.detachEvent = function(sEventId, fnFunction, oListener) {
jQuery.sap.assert(typeof (sEventId) === "string" && sEventId, "EventProvider.detachEvent: sEventId must be a non-empty string" );
jQuery.sap.assert(typeof (fnFunction) === "function", "EventProvider.detachEvent: fnFunction must be a function");
jQuery.sap.assert(!oListener || typeof (oListener) === "object", "EventProvider.detachEvent: oListener must be empty or an object");
var aEventListeners = this.mEventRegistry[sEventId];
if (!aEventListeners) {
return this;
}
var bListenerDetached = false;
//PERFOPT use array. remember length to not re-calculate over and over again
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
//PERFOPT check for identity instead of equality... avoid type conversion
if (aEventListeners[i].fFunction === fnFunction && aEventListeners[i].oListener === oListener) {
//delete aEventListeners[i];
aEventListeners.splice(i,1);
bListenerDetached = true;
break;
}
}
// If we just deleted the last registered EventHandler, remove the whole entry from our map.
if (aEventListeners.length == 0) {
delete this.mEventRegistry[sEventId];
}
if (bListenerDetached) {
// Inform interested parties about changed EventHandlers
this.fireEvent(EventProvider.M_EVENTS.EventHandlerChange, {EventId: sEventId, type: 'listenerDetached' });
}
return this;
};
/**
* Fires the given event and notifies all listeners. Listeners must not change
* the content of the event.
*
* @param {string} sEventId the event id
* @param {object} [mParameters] the parameter map
* @param {boolean} [bAllowPreventDefault] whether prevent default is allowed
* @param {boolean} [bEnableEventBubbling] whether event bubbling is enabled
* @return {sap.ui.base.EventProvider|boolean} Returns <code>this</code> to allow method chaining or
* whether the default action should be executed, when bAllowPreventDefault has been set to true
* @protected
*/
EventProvider.prototype.fireEvent = function(sEventId, mParameters, bAllowPreventDefault, bEnableEventBubbling) {
// at least in BrowserEventManager when firing events of its E_EVENTS enumeration, the type will be an integer... thus avoid this check
// jQuery.sap.assert(typeof (sEventId) == "string");
// get optional parameters right
if (typeof mParameters == "boolean") {
bEnableEventBubbling = bAllowPreventDefault;
bAllowPreventDefault = mParameters;
}
var aEventListeners = this.mEventRegistry[sEventId],
bPreventDefault = false,
oEvent, oParent, oInfo;
if (bEnableEventBubbling || (aEventListeners && jQuery.isArray(aEventListeners))) {
// this ensures no 'concurrent modification exception' occurs (e.g. an event listener deregisters itself).
aEventListeners = aEventListeners ? aEventListeners.slice() : [];
oEvent = this.oEventPool.borrowObject(sEventId, this, mParameters);
//PERFOPT use array. remember length to not re-calculate over and over again
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
oInfo = aEventListeners[i];
oInfo.fFunction.call(oInfo.oListener || this, oEvent, oInfo.oData);
}
// In case this is a bubbling event and object has a getParent method, also fire on parents
if (bEnableEventBubbling) {
oParent = this.getEventingParent();
while (oParent && !oEvent.bCancelBubble) {
aEventListeners = oParent.mEventRegistry[sEventId];
if (aEventListeners && aEventListeners instanceof Array) {
aEventListeners = aEventListeners.slice();
for (var i = 0, iL = aEventListeners.length; i < iL; i++) {
oInfo = aEventListeners[i];
oInfo.fFunction.call(oInfo.oListener || oParent, oEvent, oInfo.oData);
}
}
oParent = oParent.getEventingParent();
}
}
// Store prevent default state, before returning event to the pool
bPreventDefault = oEvent.bPreventDefault;
this.oEventPool.returnObject(oEvent);
}
// Only return prevent default result in case it has been enabled, for compatibility
if (bAllowPreventDefault) {
return !bPreventDefault;
} else {
return this;
}
};
/**
* Returns whether there are any listeners for the given event ID.
*
* @param {string} sEventId the ID of the event
* @return {boolean} whether there are any listeners
* @private
*/
EventProvider.prototype.hasListeners = function(sEventId) {
return !!this.mEventRegistry[sEventId];
};
/**
* Returns the list of events currently having listeners attached.
*
* Introduced for lightspeed support to ensure that only relevant events are attached to the LS-world.
*
* This is a static method to avoid the pollution of the Element/Control namespace.
* As the callers are limited and known and for performance reasons the internal event registry
* is returned. It contains more information than necessary, but needs no expensive conversion.
*
* @return {object} the list of events currently having listeners attached
* @private
* @static
*/
EventProvider.getEventList = function(oEventProvider) {
return oEventProvider.mEventRegistry;
};
/**
* Returns the parent in the eventing hierarchy of this object.
*
* Per default this returns null, but if eventing is used in objects, which are hierarchically
* structured, this can be overwritten to make the object hierarchy visible to the eventing and
* enables the use of event bubbling within this object hierarchy.
*
* @return {sap.ui.base.EventProvider} the parent event provider
* @protected
*/
EventProvider.prototype.getEventingParent = function() {
return null;
};
/**
* Returns a string representation of this object.
*
* In case there is no class or id information, a simple static string is returned.
* Subclasses should override this method.
*
* @return {string} a string description of this eventProvider
* @public
*/
EventProvider.prototype.toString = function() {
if ( this.getMetadata ) {
return "EventProvider " + this.getMetadata().getName();
} else {
return "EventProvider";
}
};
/**
* @see sap.ui.base.Object.prototype.destroy
* @public
*/
EventProvider.prototype.destroy = function() {
this.mEventRegistry = {};
BaseObject.prototype.destroy.apply(this, arguments);
};
return EventProvider;
}, /* bExport= */ true);
},
"sap/ui/base/Exception.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a filter for list bindings
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Exception class
*
* This is the base exception class. In contrary to the Error an Exception
* should be thrown in cases, where the exception can, and should, be handled
* within the framework, instead of causing the application to exit.
*
* The try/catch statement in JavaScript can not catch specific exceptions, so
* when catching internal exceptions you should make sure to rethrow other errors:
*
* try {
* ...
* }
* catch (oException) {
* if (oException instanceof sap.ui.base.Exception) {
* ... handle exception ...
* }
* else {
* throw oException;
* }
* }
*
* @alias sap.ui.base.Exception
*/
var Exception = function(message) {
this.name = "Exception";
this.message = message;
};
return Exception;
}, /* bExport= */ true);
},
"sap/ui/base/ExpressionParser.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define(['jquery.sap.global', 'jquery.sap.strings'], function(jQuery/* , jQuerySap1 */) {
"use strict";
/*global URI */
//SAP's Independent Implementation of "Top Down Operator Precedence" by Vaughan R. Pratt,
// see http://portal.acm.org/citation.cfm?id=512931
//Inspired by "TDOP" of Douglas Crockford which is also an implementation of Pratt's article
// see https://github.com/douglascrockford/TDOP
//License granted by Douglas Crockford to SAP, Apache License 2.0
// (http://www.apache.org/licenses/LICENSE-2.0)
//
//led = "left denotation"
//lbp = "left binding power", for values see
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
//nud = "null denotation"
//rbp = "right binding power"
var mDefaultGlobals = {
encodeURIComponent: encodeURIComponent,
Math: Math,
odata: {
fillUriTemplate: function () {
if (!URI.expand) {
jQuery.sap.require("sap.ui.thirdparty.URITemplate");
}
return URI.expand.apply(URI, arguments).toString();
},
uriEncode: function () {
var ODataUtils;
jQuery.sap.require("sap.ui.model.odata.ODataUtils");
ODataUtils = sap.ui.model.odata.ODataUtils;
return ODataUtils.formatValue.apply(ODataUtils, arguments);
}
},
RegExp: RegExp
},
mSymbols = { //symbol table
"BINDING": {
led: unexpected,
nud: function (oToken, oParser) {
return jQuery.proxy(BINDING, null, oToken.value);
}
},
"IDENTIFIER": {
led: unexpected,
nud: function (oToken, oParser) {
return jQuery.proxy(CONSTANT, null, oParser.globals[oToken.value]);
}
},
"CONSTANT": {
led: unexpected,
nud: function (oToken, oParser) {
return jQuery.proxy(CONSTANT, null, oToken.value);
}
},
".": {
lbp: 18,
led: function (oToken, oParser, fnLeft) {
return jQuery.proxy(DOT, null, fnLeft, oParser.advance("IDENTIFIER").value);
},
nud: unexpected
},
"(": {
lbp: 17,
led: function (oToken, oParser, fnLeft) {
var aArguments = [],
bFirstArgument = true;
while (oParser.current().id !== ")") {
if (bFirstArgument) {
bFirstArgument = false;
} else {
oParser.advance(","); //consume "," from predecessor argument
}
aArguments.push(oParser.expression(0));
}
oParser.advance(")");
return jQuery.proxy(FUNCTION_CALL, null, fnLeft, aArguments);
},
nud: function (oToken, oParser) {
var fnValue = oParser.expression(0);
oParser.advance(")");
return fnValue;
}
},
"!": {
lbp: 15,
led: unexpected,
nud: function (oToken, oParser) {
return jQuery.proxy(UNARY, null, oParser.expression(this.lbp),
function (x) { return !x; });
}
},
"typeof": {
lbp: 15,
led: unexpected,
nud: function (oToken, oParser) {
return jQuery.proxy(UNARY, null, oParser.expression(this.lbp),
function (x) { return typeof x; });
}
},
"?": {
lbp: 4,
led: function (oToken, oParser, fnLeft) {
var fnElse, fnThen;
fnThen = oParser.expression(this.lbp - 1);
oParser.advance(":");
fnElse = oParser.expression(this.lbp - 1);
return jQuery.proxy(CONDITIONAL, null, fnLeft, fnThen, fnElse);
},
nud: unexpected
},
")": {
led: unexpected,
nud: unexpected
},
"{": {
led: unexpected,
nud: function (oToken, oParser) {
var bFirstArgument = true,
sKey,
mMap = {},
fnValue;
while (oParser.current().id !== "}") {
if (bFirstArgument) {
bFirstArgument = false;
} else {
oParser.advance(",");
}
if (oParser.current() && oParser.current().id === "CONSTANT"
&& typeof oParser.current().value === "string") {
sKey = oParser.advance().value;
} else {
sKey = oParser.advance("IDENTIFIER").value;
}
oParser.advance(":");
fnValue = oParser.expression(0);
mMap[sKey] = fnValue;
}
oParser.advance("}");
return jQuery.proxy(MAP, null, mMap);
}
},
"}": {
lbp: -1, // Note: also terminates end of our input!
led: unexpected,
nud: unexpected
},
",": {
led: unexpected,
nud: unexpected
},
":": {
led: unexpected,
nud: unexpected
}
},
//Fix length tokens. A token being a prefix of another must come last, e.g. ! after !==
aTokens = ["===", "!==", "!", "||", "&&", ".", "(", ")", "{", "}", ":", ",", "?", "*",
"/", "%", "+", "-", "<=", "<", ">=", ">"],
rTokens;
jQuery.each(aTokens, function(i, sToken) {
aTokens[i] = jQuery.sap.escapeRegExp(sToken);
});
rTokens = new RegExp(aTokens.join("|"), "g");
addInfix("*", 14, function (x, y) { return x * y; });
addInfix("/", 14, function (x, y) { return x / y; });
addInfix("%", 14, function (x, y) { return x % y; });
addInfix("+", 13, function (x, y) { return x + y; }).nud = function (oToken, oParser) {
return jQuery.proxy(UNARY, null, oParser.expression(this.lbp),
function (x) { return +x; });
};
addInfix("-", 13, function (x, y) { return x - y; }).nud = function (oToken, oParser) {
return jQuery.proxy(UNARY, null, oParser.expression(this.lbp),
function (x) { return -x; });
};
addInfix("<=", 11, function (x, y) { return x <= y; });
addInfix("<", 11, function (x, y) { return x < y; });
addInfix(">=", 11, function (x, y) { return x >= y; });
addInfix(">", 11, function (x, y) { return x > y; });
addInfix("===", 10, function (x, y) { return x === y; });
addInfix("!==", 10, function (x, y) { return x !== y; });
addInfix("&&", 7, function (x, fnY) { return x && fnY(); }, true);
addInfix("||", 6, function (x, fnY) { return x || fnY(); }, true);
//Formatter functions to evaluate symbols like literals or operators in the expression grammar
/**
* Formatter function for an embedded binding.
* @param {number} i - the index of the binding as it appears when reading the
* expression from the left
* @param {any[]} aParts - the array of binding values
* @returns {any} the binding value
*/
function BINDING(i, aParts) {
return aParts[i];
}
/**
* Formatter function for executing the conditional operator with the given condition, "then"
* and "else" clause.
* @param {function} fnCondition - formatter function for the condition
* @param {function} fnThen - formatter function for the "then" clause
* @param {function} fnElse- formatter function for the "else" clause
* @param {any[]} aParts - the array of binding values
* @return {any} - the value of the "then" or "else" clause, depending on the value of the
* condition
*/
function CONDITIONAL(fnCondition, fnThen, fnElse, aParts) {
return fnCondition(aParts) ? fnThen(aParts) : fnElse(aParts);
}
/**
* Formatter function for any constant value such as a literal or identifier.
* @param {any} v - any value
* @returns {any} the given value
*/
function CONSTANT(v) {
return v;
}
/**
* Formatter function for member access via the dot operator.
* @param {function} fnLeft - formatter function for the left operand
* @param {string} sIdentifier - the identifier on the dot's right side
* @param {any[]} aParts - the array of binding values
* @return {any} - the left operand's member with the name
*/
function DOT(fnLeft, sIdentifier, aParts) {
var oParent = fnLeft(aParts),
vChild = oParent[sIdentifier];
// Note: jQuery.proxy() cannot handle this in case typeof oParent === "string"
return typeof vChild === "function" ? vChild.bind(oParent) : vChild;
}
/**
* Formatter function for a call to the function returned by fnLeft.
* @param {function} fnLeft - formatter function for the left operand: the function to call
* @param {function[]} aArguments - array of formatter functions for the arguments
* @param {any[]} aParts - the array of binding values
* @return {any} - the return value of the function applied to the arguments
*/
function FUNCTION_CALL(fnLeft, aArguments, aParts) {
var aResult = [];
jQuery.each(aArguments, function(i, fnArgument) {
aResult[i] = fnArgument(aParts); // evaluate argument
});
// evaluate function expression and call it
return fnLeft(aParts).apply(null, aResult);
}
/**
* Formatter function for an infix operator.
*
* @param {function} fnLeft - formatter function for the left operand
* @param {function} fnRight - formatter function for the right operand
* @param {function} fnOperator
* function taking two arguments which evaluates the infix operator
* @param {boolean} bLazy - whether the right operand is e
* @param {any[]} aParts - the array of binding values
* @return {any} - the result of the operator function applied to the two operands
*/
function INFIX(fnLeft, fnRight, fnOperator, bLazy, aParts) {
return fnOperator(fnLeft(aParts),
bLazy ? jQuery.proxy(fnRight, null, aParts) : fnRight(aParts));
}
/**
* Formatter function for an object literal.
* @param {object} mMap - map from key to formatter functions for the values
* @param {any[]} aParts - the array of binding values
* @return {object} - the resulting map
*/
function MAP(mMap, aParts) {
var mResult = {};
jQuery.each(mMap, function(sKey, fnValue) {
mResult[sKey] = fnValue(aParts); // evaluate value
});
return mResult;
}
/**
* Formatter function for a unary operator.
*
* @param {function} fnRight - formatter function for the operand
* @param {function} fnOperator
* function to evaluate the unary operator taking one argument
* @param {any[]} aParts - the array of binding values
* @return {any} - the result of the operator function applied to the operand
*/
function UNARY(fnRight, fnOperator, aParts) {
return fnOperator(fnRight(aParts));
}
/**
* Adds the infix operator with the given id, binding power and formatter function to the
* symbol table.
* @param {string} sId - the id of the infix operator
* @param {number} iBindingPower - the binding power = precedence of the infix operator
* @param {function} fnOperator - the function to evaluate the operator
* @param {boolean} [bLazy=false] - whether the right operand is lazily evaluated
* @return {object} the newly created symbol for the infix operator
*/
function addInfix(sId, iBindingPower, fnOperator, bLazy) {
mSymbols[sId] = {
lbp: iBindingPower,
led: function (oToken, oParser, fnLeft) {
//lazy evaluation is right associative: performance optimization for guard and
//default operator, e.g. true || A || B || C does not execute the || for B and C
var rbp = bLazy ? this.lbp - 1 : this.lbp;
return jQuery.proxy(INFIX, null, fnLeft, oParser.expression(rbp),
fnOperator, bLazy);
},
nud: unexpected
};
return mSymbols[sId];
}
/**
* Throws a SyntaxError with the given <code>sMessage</code> as <code>message</code>, its
* <code>at</code> property set to <code>iAt</code> and its <code>text</code> property to
* <code>sInput</code>.
* In addition, logs a corresponding error message to the console with <code>sInput</code>
* as details.
*
* @param {string} sMessage - the error message
* @param {string} sInput - the input string
* @param {number} [iAt] - the index in the input string where the error occurred; the index
* starts counting at 1 to be consistent with positions provided in tokenizer error messages.
*/
function error(sMessage, sInput, iAt) {
var oError = new SyntaxError(sMessage);
oError.at = iAt;
oError.text = sInput;
if (iAt !== undefined) {
sMessage += " at position " + iAt;
}
jQuery.sap.log.error(sMessage, sInput, "sap.ui.base.ExpressionParser");
throw oError;
}
/**
* Throws and logs an error for the unexpected token oToken.
* @param {object} oToken - the unexpected token
*/
function unexpected(oToken) {
var sToken = oToken.input.slice(oToken.start, oToken.end);
error("Unexpected " + oToken.id + (sToken !== oToken.id ? ": " + sToken : ""),
oToken.input,
oToken.start + 1 /*position for error starts counting at 1*/);
}
/**
* Computes the tokens according to the expression grammar in sInput starting at iStart and
* uses fnResolveBinding to resolve bindings embedded in the expression.
* @param {function} fnResolveBinding - the function to resolve embedded bindings
* @param {string} sInput - the string to be parsed
* @param {number} [iStart=0] - the index to start parsing
* @returns {object} Tokenization result object with the following properties
* at: the index after the last character consumed by the tokenizer in the input string
* parts: array with parts corresponding to resolved embedded bindings
* tokens: the array of tokens where each token is a tuple of ID, optional value, and
* optional source text
*/
function tokenize(fnResolveBinding, sInput, iStart) {
var aParts = [],
aTokens = [],
oTokenizer = jQuery.sap._createJSTokenizer();
/**
* Consumes the next token in the input string and pushes it to the array of tokens.
* @returns {boolean} whether a token is recognized
*/
function consumeToken() {
var ch, oBinding, iIndex, aMatches, oToken;
oTokenizer.white();
ch = oTokenizer.getCh();
iIndex = oTokenizer.getIndex();
if (/[a-z]/i.test(ch)) {
aMatches = /[a-z]\w*/i.exec(sInput.slice(iIndex));
if (aMatches[0] === "false"
|| aMatches[0] === "null"
|| aMatches[0] === "true") {
oToken = {id: "CONSTANT", value: oTokenizer.word()};
} else if (aMatches[0] === "typeof") {
oToken = {id: "typeof"};
oTokenizer.setIndex(iIndex + aMatches[0].length);
} else {
oToken = {id: "IDENTIFIER", value: aMatches[0]};
oTokenizer.setIndex(iIndex + aMatches[0].length);
}
} else if (/\d/.test(ch)
|| ch === "." && /\d/.test(sInput.charAt(oTokenizer.getIndex() + 1))) {
oToken = {id: "CONSTANT", value: oTokenizer.number()};
} else if (ch === "'" || ch === '"') {
oToken = {id: "CONSTANT", value: oTokenizer.string()};
} else if (ch === "$") {
oTokenizer.next("$");
oTokenizer.next("{"); //binding
oBinding = fnResolveBinding(sInput, oTokenizer.getIndex() - 1);
oToken = {
id: "BINDING",
value: aParts.length
};
aParts.push(oBinding.result);
oTokenizer.setIndex(oBinding.at); //go to first character after binding string
} else {
rTokens.lastIndex = iIndex;
aMatches = rTokens.exec(sInput);
if (!aMatches || aMatches.index !== iIndex) {
return false; // end of input or unrecognized character
}
oToken = {id: aMatches[0]};
oTokenizer.setIndex(iIndex + aMatches[0].length);
}
oToken.input = sInput;
oToken.start = iIndex;
oToken.end = oTokenizer.getIndex();
aTokens.push(oToken);
return true;
}
oTokenizer.init(sInput, iStart);
try {
/* eslint-disable no-empty */
while (consumeToken()) { /* deliberately empty */ }
/* eslint-enable no-empty */
} catch (e) {
if (e.name === "SyntaxError") { //handle tokenizer errors
error(e.message, e.text, e.at);
} else {
throw e;
}
}
return {
at: oTokenizer.getIndex(),
parts: aParts,
tokens: aTokens
};
}
/**
* Parses expression tokens to a result object as specified to be returned by
* {@link sap.ui.base.ExpressionParser#parse}.
* @param {object[]} aTokens - the array with the tokens
* @param {string} sInput - the expression string (used when logging errors)
* @param {object} mGlobals - the map of global variables
* @returns {object} the parse result with the following properties
* formatter: the formatter function to evaluate the expression which
* takes the parts corresponding to bindings embedded in the expression as
* parameters; undefined in case of an invalid expression
* at: the index of the first character after the expression in sInput
*/
function parse(aTokens, sInput, mGlobals) {
var fnFormatter,
iNextToken = 0,
oParser = {
advance: advance,
current: current,
expression: expression,
globals: mGlobals
},
oToken;
/**
* Returns the next token in the array of tokens and advances the index in this array.
* Throws an error if the next token's ID is not equal to the optional
* <code>sExpectedTokenId</code>.
* @param {string} [sExpectedTokenId] - the expected id of the next token
* @returns {object} - the next token or undefined if all tokens have been read
*/
function advance(sExpectedTokenId) {
var oToken = aTokens[iNextToken];
if (sExpectedTokenId) {
if (!oToken) {
error("Expected " + sExpectedTokenId + " but instead saw end of input",
sInput);
} else if (oToken.id !== sExpectedTokenId) {
error("Expected " + sExpectedTokenId + " but instead saw "
+ sInput.slice(oToken.start, oToken.end),
sInput,
oToken.start + 1);
}
}
iNextToken += 1;
return oToken;
}
/**
* Returns the next token in the array of tokens, but does not advance the index.
* @returns {object} - the next token or undefined if all tokens have been read
*/
function current() {
return aTokens[iNextToken];
}
/**
* Parse an expression starting at the current token. Throws an error if there are no more
* tokens and
*
* @param {number} rbp
* a "right binding power"
* @returns {function} The formatter function for the expression
*/
function expression(rbp) {
var fnLeft;
oToken = advance();
if (!oToken) {
error("Expected expression but instead saw end of input", sInput);
}
fnLeft = mSymbols[oToken.id].nud(oToken, oParser);
while (iNextToken < aTokens.length) {
oToken = current();
if (rbp >= (mSymbols[oToken.id].lbp || 0)) {
break;
}
advance();
fnLeft = mSymbols[oToken.id].led(oToken, oParser, fnLeft);
}
return fnLeft;
}
//TODO allow short read by passing 0 (do it if iStart is provided below)
fnFormatter = expression(-1); // -1 = "greedy read"
return {
at: current() ? current().start : undefined,
formatter: fnFormatter
};
}
/**
* The parser to parse expressions in bindings.
*
* @alias sap.ui.base.ExpressionParser
* @private
*/
return {
/**
* Parses a string <code>sInput</code> with an expression based on the syntax sketched
* below.
*
* If a start index <code>iStart</code> for parsing is provided, the input string is parsed
* starting from this index and the return value contains the index after the last
* character belonging to the expression.
*
* If <code>iStart</code> is undefined the complete string is parsed; in this case
* a <code>SyntaxError</code> is thrown if it does not comply to the expression syntax.
*
* The expression syntax is a subset of JavaScript expression syntax with the
* enhancement that the only "variable" parts in an expression are bindings.
* The following expression constructs are supported: <ul>
* <li> String literal enclosed in single or double quotes, e.g. 'foo' </li>
* <li> Null and Boolean literals: null, true, false </li>
* <li> Object and number literals, e.g. {foo:'bar'} and 3.141 </li>
* <li> Grouping, e.g. a * (b + c)</li>
* <li> Unary operators !, +, -, typeof </li>
* <li> Multiplicative Operators: *, /, % </li>
* <li> Additive Operators: +, - </li>
* <li> Relational Operators: <, >, <=, >= </li>
* <li> Strict Equality Operators: ===, !== </li>
* <li> Binary Logical Operators: &&, || </li>
* <li> Conditional Operator: ? : </li>
* <li> Member access via . operator </li>
* <li> Function call </li>
* <li> Embedded binding to refer to model contents, e.g. ${myModel>/Address/city} </li>
* <li> Global functions and objects: encodeURIComponent, Math, RegExp </li>
* </ul>
*
* @param {function} fnResolveBinding - the function to resolve embedded bindings
* @param {string} sInput - the string to be parsed
* @param {number} [iStart=0] - the index to start parsing
* @param {object} [mGlobals]
* global variables allowed in the expression as map of variable name to its value
* @returns {object} the parse result with the following properties
* result: object with the properties
* formatter: the formatter function to evaluate the expression which
* takes the parts corresponding to bindings embedded in the expression as
* parameters
* parts: the array of parts contained in the expression string which is
* empty if no parts exist
* at: the index of the first character after the expression in sInput
* @throws SyntaxError
* If the expression string is invalid or unsupported. The at property of
* the error contains the position where parsing failed.
*/
parse: function (fnResolveBinding, sInput, iStart, mGlobals) {
var oResult, oTokens;
oTokens = tokenize(fnResolveBinding, sInput, iStart);
oResult = parse(oTokens.tokens, sInput, mGlobals || mDefaultGlobals);
//TODO TDD replace !iStart by iStart === undefined
if (!iStart && oTokens.at < sInput.length) {
error("Invalid token in expression", sInput, oTokens.at);
}
if (!oTokens.parts.length) {
return {
constant: oResult.formatter(),
at: oResult.at || oTokens.at
};
}
function formatter() {
//make separate parameters for parts one (array like) parameter
// TODO stringify the result?
return oResult.formatter(arguments);
}
formatter.textFragments = true; //use CompositeBinding even if there is only one part
return {
result: {
formatter: formatter,
parts: oTokens.parts
//TODO useRawValues: true --> use JS object instead of formatted String
},
at: oResult.at || oTokens.at
};
}
};
}, /* bExport= */ true);
},
"sap/ui/base/Interface.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.Interface
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Constructs an instance of sap.ui.base.Interface which restricts access to methods marked as public.
*
* @class A class that creates an Interface for an existing class. If a class returns the interface in its constructor,
* only the defined functions will be visible, no internals of the class can be accessed.
*
* @author Malte Wedel, Daniel Brinkmann
* @version 1.28.5
* @param {sap.ui.base.Object}
* oObject the instance that needs an interface created
* @param {string[]}
* aMethods the names of the methods, that should be available on this interface
* @constructor
* @public
* @alias sap.ui.base.Interface
*/
var Interface = function(oObject, aMethods) {
// if object is null or undefined, return itself
if (!oObject) {
return oObject;
}
// this function is inline to not appear as a method on the interface
function fCreateDelegator(oObject, sMethodName) {
return function() {
// return oObject[sMethodName].apply(oObject, arguments);
var tmp = oObject[sMethodName].apply(oObject, arguments);
// to avoid to hide the implementation behind the interface you need
// to override the getInterface function in the object
return (tmp instanceof sap.ui.base.Object) ? tmp.getInterface() : tmp;
};
}
// if there are no methods return
if (!aMethods) {
return {};
}
var sMethodName;
// create functions for all delegated methods
// PERFOPT: 'cache' length of aMethods to reduce # of resolutions
for (var i = 0, ml = aMethods.length; i < ml; i++) {
sMethodName = aMethods[i];
this[sMethodName] = fCreateDelegator(oObject, sMethodName);
}
};
return Interface;
}, /* bExport= */ true);
},
"sap/ui/base/ManagedObject.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base class for all objects with managed properties and aggregations.
sap.ui.define(['jquery.sap.global', './BindingParser', './DataType', './EventProvider', './ManagedObjectMetadata', 'sap/ui/model/CompositeBinding', 'sap/ui/model/ContextBinding', 'sap/ui/model/Model', 'sap/ui/model/Type', 'jquery.sap.act', 'jquery.sap.script', 'jquery.sap.strings'],
function(jQuery, BindingParser, DataType, EventProvider, ManagedObjectMetadata, CompositeBinding, ContextBinding, Model, Type/* , jQuerySap2, jQuerySap, jQuerySap1 */) {
"use strict";
/**
* Constructs and initializes a managed object with the given <code>sId</code> and settings.
*
* If the optional <code>mSettings</code> are given, they must be a simple object
* that defines values for properties, aggregations, associations or events keyed by their name.
*
* <b>Valid Names and Value Ranges:</b>
*
* The property (key) names supported in the object literal are exactly the (case sensitive)
* names documented in the JSDoc for the properties, aggregations, associations and events
* of the current class and its base classes. Note that for 0..n aggregations and associations this
* name usually is the plural name, whereas it is the singular name in case of 0..1 relations.
*
* If a key name is ambiguous for a specific managed object class (e.g. a property has the same
* name as an event), then this method prefers property, aggregation, association and
* event in that order. To resolve such ambiguities, the keys can be prefixed with
* <code>aggregation:</code>, <code>association:</code> or <code>event:</code>
* (such keys containing a colon (':') must be quoted to be valid Javascript).
*
* The possible values for a setting depend on its kind:
* <ul>
* <li>for simple properties, the value has to match the documented type of the property (no type conversion occurs)
* <li>for 0..1 aggregations, the value has to be an instance of the aggregated type
* <li>for 0..n aggregations, the value has to be an array of instances of the aggregated type or a single instance
* <li>for 0..1 associations, an instance of the associated type or an id (string) is accepted
* <li>for 0..n associations, an array of instances of the associated type or of Ids is accepted
* <li>for events either a function (event handler) is accepted or an array of length 2
* where the first element is a function and the 2nd element is an object to invoke the method on.
* </ul>
*
* Each subclass should document the name and type of its supported settings in its constructor documentation.
*
* Besides the settings documented below, ManagedObject itself supports the following special settings:
* <ul>
* <li><code>id : <i>sap.ui.core.ID</i></code> an ID for the new instance. Some subclasses (Element, Component) require the id
* to be unique in a specific scope (e.g. an Element Id must be unique across all Elements, a Component id must
* be unique across all Components).
* <li><code>models : <i>object</i></code> a map of {@link sap.ui.model.Model} instances keyed by their model name (alias).
* Each entry with key <i>k</i> in this object has the same effect as a call <code>this.setModel(models[k], k);</code>.</li>
* <li><code>bindingContexts : <i>object</i></code> a map of {@link sap.ui.model.Context} instances keyed by their model name.
* Each entry with key <i>k</i> in this object has the same effect as a call <code>this.setBindingContext(bindingContexts[k], k);</code></li>
* <li><code>objectBindings : <i>object</i></code> a map of binding paths keyed by the corresponding model name.
* Each entry with key <i>k</i> in this object has the same effect as a call <code>this.bindObject(objectBindings[k], k);</code></li>
* </ul>
*
* @param {string} [sId] id for the new managed object; generated automatically if no non-empty id is given
* Note: this can be omitted, no matter whether <code>mSettings</code> will be given or not!
* @param {object} [mSettings] optional map/JSON-object with initial property values, aggregated objects etc. for the new object
* @param {object} [oScope] scope object for resolving string based type and formatter references in bindings
*
*
* @class Base Class that introduces some basic concepts like state management or databinding.
*
* New subclasses of ManagedObject are created with a call to {@link .extend ManagedObject.extend} and can make use
* of the following managed features:
*
* <b>Properties</b><br>
* Managed properties represent the state of a ManagedObject. They can store a single value of a simple data type
* (like 'string' or 'int'). They have a <i>name</i> (e.g. 'size') and methods to get the current value (<code>getSize</code>)
* or to set a new value (<code>setSize</code>). When a property is modified, the ManagedObject is marked as invalidated.
* A managed property can be bound against a property in a {@link sap.ui.model.Model} by using the {@link #bindProperty} method.
* Updates to the model property will be automatically reflected in the managed property and - if TwoWay databinding is active,
* changes to the managed property will be reflected in the model. An existing binding can be removed by calling {@link #unbindProperty}.
*
* If a ManagedObject is cloned, the clone will have the same values for its managed properties as the source of the
* clone - if the property wasn't bound. If it is bound, the property in the clone will be bound to the same
* model property as in the source.
*
* Details about the declaration of a managed property, the metadata that describes it and the set of methods that are automatically
* generated to access it, can be found in the documentation of the {@link sap.ui.base.ManagedObject.extend extend } method.
*
*
* <b>Aggregations</b><br>
* Managed aggregations can store one or more references to other ManagedObjects. They are a mean to control the lifecycle
* of the aggregated objects: one ManagedObject can be aggregated by at most one parent ManagedObject at any time.
* When a ManagedObject is destroyed, all aggregated objects are destroyed as well and the object itself is removed from
* its parent. That is, aggregations won't contain destroyed objects or null/undefined.
*
* Aggregations have a <i>name</i> ('e.g 'header' or 'items'), a <i>cardinality</i> ('0..1' or '0..n') and are of a specific
* <i>type</i> (which must be a subclass of ManagedObject as well or a UI5 interface). A ManagedObject will provide methods to
* set or get the aggregated object for a specific aggregation of cardinality 0..1 (e.g. <code>setHeader</code>, <code>getHeader</code>
* for an aggregation named 'header'). For an aggregation of cardinality 0..n, there are methods to get all aggregated objects
* (<code>getItems</code>), to locate an object in the aggregation (e.g. <code>indexOfItem</code>), to add, insert or remove
* a single aggregated object (<code>addItem</code>, <code>insertItem</code>, <code>removeItem</code>) or to remove or destroy
* all objects from an aggregation (<code>removeAllItems</code>, <code>destroyItems</code>).
*
* Details about the declaration of a managed aggregation, the metadata that describes it and the set of methods that are automatically
* generated to access it, can be found in the documentation of the {@link sap.ui.base.ManagedObject.extend extend} method.
*
* Aggregations of cardinality 0..n can be bound to a collection in a model by using {@link bindAggregation} (and unbound again
* using {@link #unbindAggregation}. For each context in the model collection, a corresponding object will be created in the
* managed aggregation, either by cloning a template object or by calling a factory function.
*
* Aggregations also control the databinding context of bound objects: by default, aggregated objects inherit all models
* and binding contexts from their parent object.
*
* When a ManagedObject is cloned, all aggregated objects will be cloned as well - but only if they haven't been added by
* databinding. In that case, the aggregation in the clone will be bound to the same model collection.
*
*
* <b>Associations</b><br>
* Managed associations also form a relationship between objects, but they don't define a lifecycle for the
* associated objects. They even can 'break' in the sense that an associated object might have been destroyed already
* although it is still referenced in an association. For the same reason, the internal storage for associations
* are not direct object references but only the IDs of the associated target objects.
*
* Associations have a <i>name</i> ('e.g 'initialFocus'), a <i>cardinality</i> ('0..1' or '0..n') and are of a specific <i>type</i>
* (which must be a subclass of ManagedObject as well or a UI5 interface). A ManagedObject will provide methods to set or get
* the associated object for a specific association of cardinality 0..1 (e.g. <code>setInitialFocus</code>, <code>getInitialFocus</code>).
* For an association of cardinality 0..n, there are methods to get all associated objects (<code>getRefItems</code>),
* to add, insert or remove a single associated object (<code>addRefItem</code>,
* <code>insertRefItem</code>, <code>removeRefItem</code>) or to remove all objects from an association
* (<code>removeAllRefItems</code>).
*
* Details about the declaration of a managed association, the metadata that describes it and the set of methods that are automatically
* generated to access it, can be found in the documentation of the {@link sap.ui.base.ManagedObject.extend extend} method.
*
* Associations can't be bound to the model.
*
* When a ManagedObject is cloned, the result for an association depends on the relationship between the associated target
* object and the root of the clone operation: if the associated object is part of the to-be-cloned object tree (reachable
* via aggregations from the root of the clone operation), then the cloned association will reference the clone of the
* associated object. Otherwise it will reference the same object as in the original tree.
* When a ManagedObject is destroyed, other objects that are only associated, are not affected by the destroy operation.
*
*
* <b>Events</b><br>
* Managed events provide a mean for communicating important state changes to an arbitrary number of 'interested' listeners.
* Events have a <i>name</i> and (optionally) a set of <i>parameters</i>. For each event there will be methods to add or remove an event
* listener as well as a method to fire the event. (e.g. <code>attachChange</code>, <code>detachChange</code>, <code>fireChange</code>
* for an event named 'change').
*
* Details about the declaration of a managed events, the metadata that describes it and the set of methods that are automatically
* generated to access it, can be found in the documentation of the {@link sap.ui.base.ManagedObject.extend extend} method.
*
* When a ManagedObject is cloned, all listeners registered for any event in the clone source are also registered to the
* clone. Later changes are not reflect in any direction (neither from source to clone nor vice versa).
*
*
* <a name="lowlevelapi"><b>Low Level APIs:</b></a><br>
* The prototype of ManagedObject provides several generic, low level APIs to manage properties, aggregations, associations
* and events. These generic methods are solely intended for implementing higher level, non-generic methods that manage
* a single managed property etc. (e.g. a function <code>setSize(value)</code> that sets a new value for property 'size').
* {@link sap.ui.base.ManagedObject.extend} creates default implementations of those higher level APIs for all managed aspects.
* The implementation of a subclass then can override those default implementations with a more specific implementation,
* e.g. to implement a side effect when a specific property is set or retrieved.
* It is therefore important to understand that the generic low-level methods ARE NOT SUITABLE FOR GENERIC ACCESS to the
* state of a managed object, as that would bypass the overriding higher level methods and their side effects.
*
* @extends sap.ui.base.EventProvider
* @author SAP SE
* @version 1.28.5
* @public
* @alias sap.ui.base.ManagedObject
* @experimental Since 1.11.2. ManagedObject as such is public and usable. Only the support for the optional parameter
* oScope in the constructor is still experimental and might change in future versions. Applications should not rely on it.
*/
var ManagedObject = EventProvider.extend("sap.ui.base.ManagedObject", {
metadata : {
"abstract" : true,
publicMethods : [ "getId", "getMetadata", "getModel", "setModel", "hasModel", "bindProperty", "unbindProperty", "bindAggregation", "unbindAggregation", "bindObject", "unbindObject", "getObjectBinding"],
library : "sap.ui.core", // UI Library that contains this class
properties : {
},
aggregations : {
},
associations : {},
events : {
/**
* Fired after a new value for a bound property has been propagated to the model.
* Only fired, when the binding uses a data type.
*/
"validationSuccess" : {
enableEventBubbling : true,
parameters : {
/**
* ManagedObject instance whose property initiated the model update.
*/
element : { type : 'sap.ui.base.ManagedObject' },
/**
* Name of the property for which the bound model property has been updated.
*/
property : { type : 'string' },
/**
* Data type used in the binding.
*/
type : { type : 'sap.ui.model.Type' },
/**
* New value (external representation) as propagated to the model.
*
* <b>Note: </b>the model might modify (normalize) the value again and this modification
* will be stored in the ManagedObject. The 'newValue' parameter of this event contains
* the value <b>before</b> such a normalization.
*/
newValue : { type : 'any' },
/**
* Old value (external representation) as previously stored in the ManagedObject.
*/
oldValue : { type : 'any' }
}
},
/**
* Fired when a new value for a bound property should have been propagated to the model,
* but validating the value failed with an exception.
*/
"validationError" : {
enableEventBubbling : true,
parameters : {
/**
* ManagedObject instance whose property initiated the model update.
*/
element : { type : 'sap.ui.base.ManagedObject' },
/**
* Name of the property for which the bound model property should have been been updated.
*/
property : { type : 'string' },
/**
* Data type used in the binding.
*/
type : { type : 'sap.ui.model.Type' },
/**
* New value (external representation) as parsed and validated by the binding.
*/
newValue : { type : 'any' },
/**
* Old value (external representation) as previously stored in the ManagedObject.
*/
oldValue : { type : 'any' },
/**
* Localized message describing the validation issues
*/
message: { type : 'string' }
}
},
/**
* Fired when a new value for a bound property should have been propagated to the model,
* but parsing the value failed with an exception.
*/
"parseError" : {
enableEventBubbling : true,
parameters : {
/**
* ManagedObject instance whose property initiated the model update.
*/
element : { type : 'sap.ui.base.ManagedObject' },
/**
* Name of the property for which the bound model property should have been been updated.
*/
property : { type : 'string' },
/**
* Data type used in the binding.
*/
type : { type : 'sap.ui.model.Type' },
/**
* New value (external representation) as parsed by the binding.
*/
newValue : { type : 'any' },
/**
* Old value (external representation) as previously stored in the ManagedObject.
*/
oldValue : { type : 'any' },
/**
* Localized message describing the parse error
*/
message: { type : 'string' }
}
},
/**
* Fired when a new value for a bound property should have been propagated from the model,
* but formatting the value failed with an exception.
*/
"formatError" : {
enableEventBubbling : true,
parameters : {
/**
* ManagedObject instance whose property should have received the model update.
*/
element : { type : 'sap.ui.base.ManagedObject' },
/**
* Name of the property for which the binding should have been updated.
*/
property : { type : 'string' },
/**
* Data type used in the binding (if any).
*/
type : { type : 'sap.ui.model.Type' },
/**
* New value (model representation) as propagated from the model.
*/
newValue : { type : 'any' },
/**
* Old value (external representation) as previously stored in the ManagedObject.
*/
oldValue : { type : 'any' }
}
}
},
specialSettings : {
/**
* Unique ID of this instance.
* If not given, a so called autoID will be generated by the framework.
* AutoIDs use a unique prefix that must not be used for Ids that the application (or other code) creates.
* It can be configured option 'autoIDPrefix', see {@link sap.ui.core.Configuration}.
*/
id : true,
//id : {type : "string", group : "Identification", defaultValue : '', readOnly : true}
/**
* A map of model instances to which the object should be attached.
* The models are keyed by their model name. For the default model, String(undefined) is expected.
*/
models : true,
/**
* A map of model instances to which the object should be attached.
* The models are keyed by their model name. For the default model, String(undefined) is expected.
*/
bindingContexts : true,
/**
* A map of model instances to which the object should be attached.
* The models are keyed by their model name. For the default model, String(undefined) is expected.
*/
objectBindings : true,
/**
* Used by ManagedObject.create.
*/
Type : true
}
},
constructor : function(sId, mSettings, oScope) {
EventProvider.call(this); // no use to pass our arguments
if (typeof (sId) != "string" && arguments.length > 0) {
// shift arguments in case sId was missing, but mSettings was given
oScope = mSettings;
mSettings = sId;
if (mSettings && mSettings.id) {
sId = mSettings["id"];
} else {
sId = null;
}
}
if (!sId) {
sId = this.getMetadata().uid() || jQuery.sap.uid();
} else {
var preprocessor = ManagedObject._fnIdPreprocessor;
sId = (preprocessor ? preprocessor.call(this, sId) : sId);
var oType = DataType.getType("sap.ui.core.ID");
if (!oType.isValid(sId)) {
throw new Error("\"" + sId + "\" is not a valid ID.");
}
}
this.sId = sId;
// managed object interface
// create an empty property bag that uses a map of defaultValues as its prototype
this.mProperties = this.getMetadata().createPropertyBag();
this.mAggregations = {};
this.mAssociations = {};
this.mMethods = {};
// private properties
this.oParent = null;
this.aDelegates = [];
this.aBeforeDelegates = [];
this.iSuppressInvalidate = 0;
this.oPropagatedProperties = {oModels:{}, oBindingContexts:{}};
this.mSkipPropagation = {};
// data binding
this.oModels = {};
this.oBindingContexts = {};
this.mElementBindingContexts = {};
this.mBindingInfos = {};
this.sBindingPath = null;
this.mBindingParameters = null;
this.mBoundObjects = {};
// apply the owner id if defined
this._sOwnerId = ManagedObject._sOwnerId;
// make sure that the object is registered before initializing
// and to deregister the object in case of errors
try {
// registers the object in the Core
if (this.register) {
this.register();
}
// TODO: generic concept for init hooks?
if ( this._initCompositeSupport ) {
this._initCompositeSupport(mSettings);
}
// Call init method here instead of specific Controls constructor.
if (this.init) {
this.init();
}
// apply the settings
this.applySettings(mSettings, oScope);
} catch (ex) {
// unregisters the object in the Core
if (this.deregister) {
this.deregister();
}
// forward the exception
throw ex;
}
}
}, /* Metadata constructor */ ManagedObjectMetadata);
/**
* Returns the metadata for the ManagedObject class.
*
* @return {sap.ui.base.ManagedObjectMetadata} Metadata for the ManagedObject class.
* @static
* @public
* @name sap.ui.base.ManagedObject.getMetadata
* @function
*/
/**
* Returns the metadata for the class that this object belongs to.
*
* @return {sap.ui.base.ManagedObjectMetadata} Metadata for the class of the object
* @public
* @name sap.ui.base.ManagedObject#getMetadata
* @function
*/
/**
* Defines a new subclass of ManagedObject with name <code>sClassName</code> and enriches it with
* the information contained in <code>oClassInfo</code>.
*
* <code>oClassInfo</code> can contain the same information that {@link sap.ui.base.Object.extend} already accepts,
* plus the following new properties in the 'metadata' object literal:
*
* <ul>
* <li><code>library : <i>string</i></code></li>
* <li><code>properties : <i>object</i></code></li>
* <li><code>aggregations : <i>object</i></code></li>
* <li><code>defaultAggregation : <i>string</i></code></li>
* <li><code>associations : <i>object</i></code></li>
* <li><code>events : <i>object</i></code></li>
* <li><code>specialSettings : <i>object</i></code>// this one is still experimental and not for public usage!</li>
* </ul>
*
* Each of these properties is explained in more detail lateron.
*
* Example:
* <pre>
* ManagedObect.extend('sap.mylib.MyClass', {
* metadata : {
* library: 'sap.mylib',
* properties : {
* width: 'sap.ui.core.CSSSize',
* height: { type: 'sap.ui.core.CSSSize', defaultValue: '100%' }
* },
* aggregations : {
* header : { type: 'sap.mylib.FancyHeader', multiple : false }
* items : 'sap.ui.core.Control'
* },
* defaultAggregation : 'items',
* associations : {
* initiallyFocused : { type: 'sap.ui.core.Control' }
* },
* events: {
* beforeOpen : {
* parameters : {
* opener : 'sap.ui.core.Control'
* }
* }
* },
* },
*
* init: function() {
* }
*
* }); // end of 'extend' call
* </pre>
*
* Detailed explanation of properties<br>
*
*
* <b>'library'</b> : <i>string</i><br>
* Name of the library that the new subclass should belong to. If the subclass is a control or element, it will
* automatically register with that library so that authoring tools can discover it.
* By convention, the name of the subclass should have the library name as a prefix, e.g. 'sap.ui.commons.Panel' belongs
* to library 'sap.ui.commons'.
*
*
* <b>'properties'</b> : <i>object</i><br>
* An object literal whose properties each define a new managed property in the ManagedObject subclass.
* The value can either be a simple string which then will be assumed to be the type of the new property or it can be
* an object literal with the following properties
* <ul>
* <li><code>type: <i>string</i></code> type of the new property. Must either be one of the built-in types 'string', 'boolean', 'int', 'float', 'object' or 'any', or a
* type created and registered with {@link sap.ui.base.DataType.createType} or an array type based on one of the previous types.</li>
* <li><code>group: ...</li>
* <li><code>defaultValue: <i>any</i></code> the default value for the property or null if there is no defaultValue.</li>
* <li><code>bindable: <i>boolean|string</i></code> (either can be omitted or set to the boolean value <code>true</code> or the magic string 'bindable')
* If set to <code>true</code> or 'bindable', additional named methods <code>bind<i>Name</i> and <code>unbind<i>Name</i></code> are generated as convenience.
* Despite its name, setting this flag is not mandatory to make the managed property bindable. The generic methods {@link #bindProperty} and
* {@link #unbindProperty} can always be used. </li>
* </ul>
* Property names should use camelCase notation, start with a lowercase letter and only use characters from the set [a-zA-Z0-9_$].
* If an aggregation in the literal is preceded by a JSDoc comment (doclet) and if the UI5 plugin and template are used for JSDoc3 generation, the doclet will
* be used as generic documentation of the aggregation.
*
* For each public property 'foo', the following methods will be created by the "extend" method and will be added to the
* prototype of the subclass:
* <ul>
* <li>getFoo() - returns the current value of property 'foo'. Internally calls {@link #getProperty}
* <li>setFoo(v) - sets 'v' as the new value of property 'foo'. Internally calls {@link #setProperty}
* <li>bindFoo(c) - (only if property was defined to be 'bindable'): convenience function that wraps {@link #bindProperty}
* <li>unbindFoo() - (only if property was defined to be 'bindable'): convenience function that wraps {@link #unbindProperty}
* </ul>
*
*
* <b>'aggregations'</b> : <i>object</i><br>
* An object literal whose properties each define a new aggregation in the ManagedObject subclass.
* The value can either be a simple string which then will be assumed to be the type of the new aggregation or it can be
* an object literal with the following properties
* <ul>
* <li><code>type: <i>string</i></code> type of the new aggregation. must be the full global name of a ManagedObject subclass (in dot notation, e.g. 'sap.m.Button')</li>
* <li><code>[multiple]: <i>boolean</i></code> whether the aggregation is a 0..1 (false) or a 0..n aggregation (true), defaults to true </li>
* <li><code>[singularName]: <i>string</i></code>. Singular name for 0..n aggregations. For 0..n aggregations the name by convention should be the plural name.
* Methods affecting multiple objects in an aggregation will use the plural name (e.g. getItems(), whereas methods that deal with a single object will use
* the singular name (e.g. addItem). The framework knows a set of common rules for building plural form of English nouns and uses these rules to determine
* a singular name on its own. if that name is wrong, a singluarName can be specified with this property. </li>
* <li>[visibility]: <i>string</i></code> either 'hidden' or 'public', defaults to 'public'. Aggregations that belong to the API of a class must be 'public' whereas
* 'hidden' aggregations typically are used for the implementation of composite classes (e.g. composite controls) </li>
* <li><code>bindable: <i>boolean|string</i></code> (either can be omitted or set to the boolean value <code>true</code> or the magic string 'bindable')
* If set to <code>true</code> or 'bindable', additional named methods <code>bind<i>Name</i> and <code>unbind<i>Name</i></code> are generated as convenience.
* Despite its name, setting this flag is not mandatory to make the managed aggregation bindable. The generic methods {@link #bindAggregation} and
* {@link #unbindAggregation} can always be used. </li>
* </ul>
* Aggregation names should use camelCase notation, start with a lowercase letter and only use characters from the set [a-zA-Z0-9_$].
* The name for a hidden aggregations might start with an underscore.
* If an aggregation in the literal is preceded by a JSDoc comment (doclet) and if the UI5 plugin and template are used for JSDoc3 generation, the doclet will
* be used as generic documentation of the aggregation.
*
* For each public aggregation 'item' of cardinality 0..1, the following methods will be created by the "extend" method and will be added to the
* prototype of the subclass:
* <ul>
* <li>getItem() - returns the current value of aggregation 'item'. Internally calls {@link #getAggregation} with a default value of <code>undefined</code></li>
* <li>setItem(o) - sets 'o' as the new aggregated object in aggregation 'item'. Internally calls {@link #setAggregation}</li>
* <li>destroyItem(o) - destroy a currently aggregated object in aggregation 'item' and clears the aggregation. Internally calls {@link #destroyAggregation}</li>
* <li>bindItem(c) - (only if aggregation was defined to be 'bindable'): convenience function that wraps {@link #bindAggregation}</li>
* <li>unbindItem() - (only if aggregation was defined to be 'bindable'): convenience function that wraps {@link #unbindAggregation}</li>
* </ul>
* For a public aggregation 'items' of cardinality 0..n, the following methods will be created:
* <ul>
* <li>getItems() - returns an array with the objects contained in aggregation 'items'. Internally calls {@link #getAggregation} with a default value of <code>[]</code></li>
* <li>addItem(o) - adds an object as last element in the aggregation 'items'. Internally calls {@link #addAggregation}</li>
* <li>insertItem(o,p) - inserts an object into the aggregation 'items'. Internally calls {@link #insertAggregation}</li>
* <li>removeItem(v) - removes an object from the aggregation 'items'. Internally calls {@link #removeAggregation}</li>
* <li>removeItems() - removes all object from the aggregation 'items'. Internally calls {@link #removeAllAggregation}</li>
* <li>destroyItems() - destroy all currently aggregated objects in aggregation 'items' and clears the aggregation. Internally calls {@link #destroyAggregation}</li>
* <li>bindItems(c) - (only if aggregation was defined to be 'bindable'): convenience function that wraps {@link #bindAggregation}</li>
* <li>unbindItems() - (only if aggregation was defined to be 'bindable'): convenience function that wraps {@link #unbindAggregation}</li>
* </ul>
* For private or hidden aggregations, no methods are generated.
*
*
* <b>'defaultAggregation'</b> : <i>string</i><br>
* When specified, the default aggregation must match the name of one of the aggregations defined for the new subclass (either own or inherited).
* The named aggregation will be used in contexts where no aggregation is specified. E,g. when an object in an XMLView embeds other objects without
* naming an aggregation, as in the following example:
* <pre>
* &lt;!-- assuming the defaultAggregation for Dialog is 'content' -->
* &lt;Dialog>
* &lt;Text/>
* &lt;Button/>
* &lt;/Dialog>
* </pre>
*
*
* <b>'associations'</b> : <i>object</i><br>
* An object literal whose properties each define a new association of the ManagedObject subclass.
* The value can either be a simple string which then will be assumed to be the type of the new association or it can be
* an object literal with the following properties
* <ul>
* <li><code>type: <i>string</i></code> type of the new association</li>
* <li><code>multiple: <i>boolean</i></code> whether the association is a 0..1 (false) or a 0..n association (true), defaults to false(1) for associations</li>
* <li><code>[singularName]: <i>string</i></code>. Singular name for 0..n associations. For 0..n associations the name by convention should be the plural name.
* Methods affecting multiple objects in an association will use the plural name (e.g. getItems(), whereas methods that deal with a single object will use
* the singular name (e.g. addItem). The framework knows a set of common rules for building plural form of English nouns and uses these rules to determine
* a singular name on its own. if that name is wrong, a singluarName can be specified with this property.</li>
* </ul>
* Association names should use camelCase notation, start with a lowercase letter and only use characters from the set [a-zA-Z0-9_$].
* If an association in the literal is preceded by a JSDoc comment (doclet) and if the UI5 plugin and template are used for JSDoc3 generation, the doclet will
* be used as generic documentation of the association.
*
* For each association 'ref' of cardinality 0..1, the following methods will be created by the "extend" method and will be added to the
* prototype of the subclass:
* <ul>
* <li>getRef() - returns the current value of association 'item'. Internally calls {@link #getAssociation} with a default value of <code>undefined</code></li>
* <li>setRef(o) - sets 'o' as the new associated object in association 'item'. Internally calls {@link #setAssociation}</li>
* </ul>
* For a public association 'refs' of cardinality 0..n, the following methods will be created:
* <ul>
* <li>getRefs() - returns an array with the objects contained in association 'items'. Internally calls {@link #getAssociation} with a default value of <code>[]</code></li>
* <li>addRef(o) - adds an object as last element in the association 'items'. Internally calls {@link #addAssociation}</li>
* <li>removeRef(v) - removes an object from the association 'items'. Internally calls {@link #removeAssociation}</li>
* <li>removeAllRefs() - removes all objects from the association 'items'. Internally calls {@link #removeAllAssociation}</li>
* </ul>
*
*
* <b>'events'</b> : <i>object</i><br>
* An object literal whose properties each define a new event of the ManagedObject subclass.
* The value can either be a simple string which then will be assumed to be the type of the new association or it can be
* an object literal with the following properties
* <ul>
* <li><code>allowPreventDefault: <i>boolean</i></code> whether the event allows to prevented the default behavior of the event source</li>
* <li><code>parameters: <i>object</i></code> an object literal that describes the parameters of this event. </li>
* </ul>
* Event names should use camelCase notation, start with a lowercase letter and only use characters from the set [a-zA-Z0-9_$].
* If an event in the literal is preceded by a JSDoc comment (doclet) and if the UI5 plugin and template are used for JSDoc3 generation, the doclet will be used
* as generic documentation of the event.
*
* For each event 'Some' the following methods will be created by the "extend" method and will be added to the
* prototype of the subclass:
* <ul>
* <li>attachSome(fn,o) - registers a listener for the event. Internally calls {@link #attachEvent}</li>
* <li>detachSome(fn,o) - deregisters a listener for the event. Internally calls {@link #detachEvent}</li>
* <li>fireSome() - fire the event. Internally calls {@link #fireEvent}</li>
* </ul>
*
*
* <b>'specialSettings'</b> : <i>object</i><br>
* Special settings are an experimental feature and MUST NOT BE USED by controls or applications outside of the sap.ui.core project.
*
* @param {string} sClassName name of the class to be created
* @param {object} [oClassInfo] object literal with informations about the class
* @param {function} [FNMetaImpl] constructor function for the metadata object. If not given, it defaults to sap.ui.core.ManagedObjectMetadata.
* @return {function} the created class / constructor function
*
* @public
* @static
* @experimental Since 1.27.0 Support for 'specialSettings' is experimental and might be modified or removed in future versions.
* They must not be used in any way outside of the sap.ui.core library. Code outside sap.ui.core must not declare special settings
* nor must it try to retrieve / evaluate metadata for such settings.
* @name sap.ui.base.ManagedObject.extend
* @function
*/
/**
* Creates a new ManagedObject from the given data.
*
* If vData is a managed object already, that object is returned.
* If vData is an object (literal), then a new object is created with vData as settings.
* The type of the object is either determined by a "Type" entry in the vData or
* by a type information in the oKeyInfo object
* @param {sap.ui.base.ManagedObject|object} vData the data to create the object from
* @param {object} oKeyInfo
* @public
* @static
*/
ManagedObject.create = function(vData, oKeyInfo) {
if ( !vData || vData instanceof ManagedObject || typeof vData !== "object" || vData instanceof String) {
return vData;
}
function getClass(vType) {
if ( typeof vType === "function" ) {
return vType;
}
if (typeof vType === "string" ) {
return jQuery.sap.getObject(vType);
}
}
var fnClass = getClass(vData.Type) || getClass(oKeyInfo && oKeyInfo.type);
if ( typeof fnClass === "function" ) {
return new fnClass(vData);
}
// we don't know how to create the ManagedObject from vData, so fail
// extension points could be integrated here
var message = "Don't know how to create a ManagedObject from " + vData + " (" + (typeof vData) + ")";
jQuery.sap.log.fatal(message);
throw new Error(message);
};
/**
* A global preprocessor for the ID of a ManagedObject (used internally).
* If set, this function will be called before the ID is applied to any ManagedObject.
* If the original ID was empty, the hook will not be called (to be discussed).
*
* The expected signature is <code>function(sId)</code>, and <code>this</code> will
* be the current ManagedObject.
*
* @return new ID of the ManagedObject
* @type function
* @private
*/
ManagedObject._fnIdPreprocessor = null;
/**
* A global preprocessor for the settings of a ManagedObject (used internally).
* If set, this function will be called before the settings are applied to any ManagedObject.
* If the original settings are empty, the hook will not be called (to be discussed).
*
* The expected signature is <code>function(mSettings)</code>, and <code>this</code> will
* be the current ManagedObject.
*
* @type function
* @private
*/
ManagedObject._fnSettingsPreprocessor = null;
ManagedObject.runWithPreprocessors = function(fn, oPreprocessors) {
jQuery.sap.assert(typeof fn === "function", "fn must be a function");
jQuery.sap.assert(!oPreprocessors || typeof oPreprocessors === "object", "oPreprocessors must be an object");
var oOldPreprocessors = { id : this._fnIdPreprocessor, settings : this._fnSettingsPreprocessor };
oPreprocessors = oPreprocessors || {};
this._fnIdPreprocessor = oPreprocessors.id;
this._fnSettingsPreprocessor = oPreprocessors.settings;
try {
var result = fn.call();
this._fnIdPreprocessor = oOldPreprocessors.id;
this._fnSettingsPreprocessor = oOldPreprocessors.settings;
return result;
} catch (e) {
this._fnIdPreprocessor = oOldPreprocessors.id;
this._fnSettingsPreprocessor = oOldPreprocessors.settings;
throw e;
}
};
/*
* Returns the Id of the Component in whose context the given ManagedObject has been created.
*
* Might return <code>undefined</code> or <code>null</code> when no owner
* has been recorded for the given object. See {@link sap.ui.core.Component.getOwnerIdFor Component.getOwnerIdFor}
* for detailed constraints.
*
* @deprecated Since 1.25.1. Use sap.ui.core.Component.getOwnerIdFor or sap.ui.core.Component.getOwnerComponentFor instead.
*/
ManagedObject.getOwnerIdFor = function(oObject) {
jQuery.sap.log.error("[Deprecated] The private method sap.ui.base.ManagedObject.getOwnerIdFor must no longer be used. Use the public sap.ui.core.Component.getOwnerForId instead.");
return oObject && oObject._sOwnerId;
};
/*
* Redirect to new functionality
* @deprecated Since 1.25.1. Use sap.ui.core.Component.runAsOwner instead.
*/
ManagedObject.runWithOwner = function(fn, oOwner) {
jQuery.sap.log.error("[Deprecated] The private method sap.ui.base.ManagedObject.runWithOwner must no longer be used. Use the public sap.ui.core.Component.runAsOwner instead.");
if ( oOwner && typeof oOwner.runAsOwner === "function" ) {
oOwner.runAsOwner(fn);
} else {
throw new Error("trying to execute a function with a non-suitable owner " + oOwner + ". See the deprecation hint in the console.");
}
};
/**
* Sets all the properties, aggregations, associations and event handlers as given in
* the object literal <code>mSettings</code>. If a property, aggregation, etc.
* is not listed in <code>mSettings</code>, then its value is not changed by this method.
*
* For properties and 0..1 aggregations/associations, any given setting overwrites
* the current value. For 0..n aggregations, the given values are appended; event
* listeners are registered in addition to existing ones.
*
* For the possible keys and values in <code>mSettings</code> see the general
* documentation in {@link sap.ui.base.ManagedObject} or the specific documentation
* of the constructor of the concrete managed object class.
*
* @param {object} mSettings the settings to apply to this managed object
* @param {object} [oScope] Scope object to resolve types and formatters
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @public
* @experimental Since 1.11.2 support for the scope object for resolving string based type
* and formatter references in bindings is still experimental
*/
ManagedObject.prototype.applySettings = function(mSettings, oScope) {
// PERFOPT: don't retrieve (expensive) JSONKeys if no settings are given
if ( !mSettings || jQuery.isEmptyObject(mSettings) ) {
return this;
}
var that = this,
oMetadata = this.getMetadata(),
mValidKeys = oMetadata.getJSONKeys(), // UID names required, they're part of the documented contract of applySettings
makeObject = ManagedObject.create,
preprocessor = ManagedObject._fnSettingsPreprocessor,
sKey, oValue, oKeyInfo;
// add all given objects to the given aggregation. nested arrays are flattened
// (might occur e.g. in case of content from an extension point)
function addAllToAggregation(aObjects) {
for (var i = 0, len = aObjects.length; i < len; i++) {
var vObject = aObjects[i];
if ( jQuery.isArray(vObject) ) {
addAllToAggregation(vObject);
} else {
that[oKeyInfo._sMutator](makeObject(vObject, oKeyInfo));
}
}
}
// call the preprocessor if it has been defined
preprocessor && preprocessor.call(this, mSettings); // TODO: decide whether to call for empty settings as well?
// process models
if ( mSettings.models ) {
if ( typeof mSettings.models !== "object" ) {
throw new Error("models must be a simple object");
}
if ( mSettings.models instanceof Model) {
this.setModel(mSettings.models);
} else {
for (sKey in mSettings.models ) {
this.setModel(mSettings.models[sKey], sKey === "undefined" ? undefined : sKey);
}
}
delete mSettings.models;
}
//process BindingContext
if ( mSettings.bindingContexts ) {
if ( typeof mSettings.bindingContexts !== "object" ) {
throw new Error("bindingContexts must be a simple object");
}
if ( mSettings.bindingContexts instanceof sap.ui.model.Context) {
this.setBindingContext(mSettings.bindingContexts);
} else {
for (sKey in mSettings.bindingContexts ) {
this.setBindingContext(mSettings.bindingContexts[sKey], sKey === "undefined" ? undefined : sKey);
}
}
delete mSettings.bindingContexts;
}
//process object bindings
if ( mSettings.objectBindings ) {
if ( typeof mSettings.objectBindings !== "string" && typeof mSettings.objectBindings !== "object" ) {
throw new Error("binding must be a string or simple object");
}
if ( typeof mSettings.objectBindings === "string" || mSettings.objectBindings.path ) { // excludes "path" as model name
this.bindObject(mSettings.objectBindings);
} else {
for (var sKey in mSettings.objectBindings ) {
mSettings.objectBindings.model = sKey;
this.bindObject(mSettings.objectBindings[sKey]);
}
}
delete mSettings.objectBindings;
}
// process all settings
// process settings
for (sKey in mSettings) {
oValue = mSettings[sKey];
// get info object for the key
if ( (oKeyInfo = mValidKeys[sKey]) !== undefined ) {
var oBindingInfo;
switch (oKeyInfo._iKind) {
case 0: // PROPERTY
oBindingInfo = this.extractBindingInfo(oValue, oScope);
if (oBindingInfo && typeof oBindingInfo === "object") {
this.bindProperty(sKey, oBindingInfo);
} else {
this[oKeyInfo._sMutator](oBindingInfo || oValue);
}
break;
case 1: // SINGLE_AGGREGATION
oBindingInfo = oKeyInfo.altTypes && this.extractBindingInfo(oValue, oScope);
if ( oBindingInfo && typeof oBindingInfo === "object" ) {
this.bindProperty(sKey, oBindingInfo);
} else {
if (jQuery.isArray(oValue)){
// assumption: we have an extensionPoint here which is always an array, even if it contains a single control
if (oValue.length > 1){
jQuery.sap.log.error("Tried to add an array of controls to a single aggregation");
}
oValue = oValue[0];
}
this[oKeyInfo._sMutator](makeObject(oBindingInfo || oValue, oKeyInfo));
}
break;
case 2: // MULTIPLE_AGGREGATION
oBindingInfo = this.extractBindingInfo(oValue, oScope);
if (oBindingInfo && typeof oBindingInfo === "object" ) {
this.bindAggregation(sKey, oBindingInfo);
} else {
oValue = oBindingInfo || oValue; // could be an unescaped string if altTypes contains 'string'
if ( oValue ) {
addAllToAggregation(jQuery.isArray(oValue) ? oValue : [oValue]); // wrap a single object as array
}
}
break;
case 3: // SINGLE_ASSOCIATION
this[oKeyInfo._sMutator](oValue);
break;
case 4: // MULTIPLE_ASSOCIATION
if ( oValue && !jQuery.isArray(oValue) ) {
oValue = [oValue];
}
if ( oValue ) {
for (var i = 0,l = oValue.length; i < l; i++) {
this[oKeyInfo._sMutator](oValue[i]);
}
}
break;
case 5: // EVENT
if ( typeof oValue == "function" ) {
this[oKeyInfo._sMutator](oValue);
} else {
this[oKeyInfo._sMutator](oValue[0], oValue[1], oValue[2]);
}
break;
case -1: // SPECIAL_SETTING
// No assert
default:
break;
}
} else {
// there must be no unknown settings
jQuery.sap.assert(false, "ManagedObject.apply: encountered unknown setting '" + sKey + "' for class '" + oMetadata.getName() + "' (value:'" + oValue + "')");
}
}
return this;
};
/**
* Returns a simple string representation of this managed object.
*
* Mainly useful for tracing purposes.
* @public
* @return {string} a string description of this managed object
*/
ManagedObject.prototype.toString = function() {
return "ManagedObject " + this.getMetadata().getName() + "#" + this.getId();
};
/**
* Returns the object's Id.
*
* @return {string} the objects's Id.
* @public
*/
ManagedObject.prototype.getId = function() {
return this.sId;
};
// ######################################################################################################
// Properties
// ######################################################################################################
/**
* Sets the given value for the given property after validating and normalizing it,
* marks this object as changed.
*
* If the value is not valid with regard to the declared data type of the property,
* an Error is thrown (see {@link #validateProperty}. If the validated and normalized
* <code>oValue</code> equals the current value of the property, the internal state of
* this object is not changed. If the value changes, it is stored internally and
* the {@link #invalidate} method is called on this object. In the case of TwoWay
* databinding, the bound model is informed about the property change.
*
* Note that ManagedObject only implements a single level of change tracking: if a first
* call to setProperty recognizes a change, 'invalidate' is called. If another call to
* setProperty reverts that change, invalidate() will be called again, the new status
* is not recognized as being 'clean' again.
*
* <b>Note:</b> This method is a low level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically set a property.
* Use the concrete method set<i>XYZ</i> for property 'XYZ' or the generic {@link #applySettings} instead.
*
* @param {string} sPropertyName name of the property to set
* @param {any} oValue value to set the property to
* @param {boolean} [bSuppressInvalidate] if true, the managed object is not marked as changed
* @returns {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.setProperty = function(sPropertyName, oValue, bSuppressInvalidate) {
// check for a value change
var oOldValue = this.mProperties[sPropertyName];
// value validation
oValue = this.validateProperty(sPropertyName, oValue);
if (jQuery.sap.equal(oOldValue, oValue)) {
return this;
} // no change
// set suppress invalidate flag
if (bSuppressInvalidate) {
//Refresh only for property changes with suppressed invalidation (others lead to rerendering and refresh is handled there)
jQuery.sap.act.refresh();
this.iSuppressInvalidate++;
}
// change the property (and invalidate if the rendering should be updated)
this.mProperties[sPropertyName] = oValue;
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// check whether property is bound and update model in case of two way binding
this.updateModelProperty(sPropertyName, oValue, oOldValue);
// prototype for generic property change events
// TODO: THINK ABOUT CONFIGURATION TO ENABLE THIS
EventProvider.prototype.fireEvent.call(this, "_change", {
"id": this.getId(),
"name": sPropertyName,
"oldValue": oOldValue,
"newValue": oValue
});
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
/**
* Returns the value for the property with the given <code>sPropertyName</code>
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically retrieve the value of a property.
* Use the concrete method get<i>XYZ</i> for property 'XYZ' instead.
*
* @param {string} sPropertyName the name of the property
* @returns {any} the value of the property
* @protected
*/
ManagedObject.prototype.getProperty = function(sPropertyName) {
var oValue = this.mProperties[sPropertyName],
oProperty = this.getMetadata().getProperty(sPropertyName),
oType;
if (!oProperty) {
throw new Error("Property \"" + sPropertyName + "\" does not exist in " + this);
}
oType = DataType.getType(oProperty.type);
// If property has an array type, clone the array to avoid modification of original data
if (oType instanceof DataType && oType.isArrayType() && jQuery.isArray(oValue)) {
oValue = oValue.slice(0);
}
// If property is of type String instead of string, convert with valueOf()
if (oValue instanceof String) {
oValue = oValue.valueOf();
}
return oValue;
};
/**
* Checks whether the given value is of the proper type for the given property name.
*
* In case <code>null</code> or <code>undefined</code> is passed, the default value for
* this property is used as value. If no default value is defined for the property, the
* default value of the type of the property is used.
*
* If the property has a data type that is an instance of sap.ui.base.DataType and if
* a <code>normalize</code> function is defined for that type, that function will be
* called with the resulting value as only argument. The result of the function call is
* then used instead of the raw value.
*
* This method is called by {@link #setProperty}. In many cases, subclasses of
* ManagedObject don't need to call it themselves.
*
* @param {string} sPropertyName the name of the property
* @param {any} oValue the value
* @return {any} the normalized value for the passed value or for the default value if null or undefined was passed
* @throws Error if no property with the given name is found or the given value does not fit to the property type
* @protected
*/
ManagedObject.prototype.validateProperty = function(sPropertyName, oValue) {
var oProperty = this.getMetadata().getProperty(sPropertyName),
oType;
if (!oProperty) {
throw new Error("Property \"" + sPropertyName + "\" does not exist in " + this);
}
oType = DataType.getType(oProperty.type);
// If property has an array type, clone the array to avoid modification of original data
if (oType instanceof DataType && oType.isArrayType() && jQuery.isArray(oValue)) {
oValue = oValue.slice(0);
}
// In case null is passed as the value return the default value, either from the property or from the type
if (oValue === null || oValue === undefined) {
if (oProperty.defaultValue !== null) {
oValue = oProperty.defaultValue;
} else {
oValue = oType.getDefaultValue();
}
} else if (oType instanceof DataType) {
// Implicit casting for string only, other types are causing errors
if (oType.getName() == "string") {
if (!(typeof oValue == "string" || oValue instanceof String)) {
oValue = "" + oValue;
}
} else if (oType.getName() == "string[]") {
// For compatibility convert string values to array with single entry
if (typeof oValue == "string") {
oValue = [oValue];
}
if (!jQuery.isArray(oValue)) {
throw new Error("\"" + oValue + "\" is of type " + typeof oValue + ", expected string[]" +
" for property \"" + sPropertyName + "\" of " + this);
}
for (var i = 0; i < oValue.length; i++) {
if (!typeof oValue[i] == "string") {
oValue[i] = "" + oValue[i];
}
}
} else if (!oType.isValid(oValue)) {
throw new Error("\"" + oValue + "\" is of type " + typeof oValue + ", expected " +
oType.getName() + " for property \"" + sPropertyName + "\" of " + this);
}
} else if (!(oValue in oType)) { // Enumeration
throw new Error("\"" + oValue + "\" is not a valid entry of the enumeration for property \"" + sPropertyName + "\" of " + this);
}
// Normalize the value (if a normalizer was set using the setNormalizer method on the type)
if (oType && oType.normalize && typeof oType.normalize === "function") {
oValue = oType.normalize(oValue);
}
return oValue;
};
/**
* Returns the origin info for the value of the given property.
*
* The origin info might contain additional information for translatable
* texts. The bookkeeping of this information is not active by default and must be
* activated by configuration. Even then, it might not be present for all properties
* and their values depending on where the value came form.
*
* @param {string} sPropertyName the name of the property
* @return {object} a map of properties describing the origin of this property value or null
* @public
*/
ManagedObject.prototype.getOriginInfo = function(sPropertyName) {
var oValue = this.mProperties[sPropertyName];
if (!(oValue instanceof String && oValue.originInfo)) {
return null;
}
return oValue.originInfo;
};
// ######################################################################################################
// Associations
// ######################################################################################################
/**
* Sets the associatied object for the given managed association of cardinality '0..1' and
* marks this ManagedObject as changed.
*
* The associated object can either be given by itself or by its id. If <code>null</code> or
* <code>undefined</code> is given, the association is cleared.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically set an object in an association.
* Use the concrete method set<i>XYZ</i> for association 'XYZ' or the generic {@link #applySettings} instead.
*
* @param {string}
* sAssociationName name of the association
* @param {string | sap.ui.base.ManagedObject}
* sId the ID of the managed object that is set as an association, or the managed object itself or null
* @param {boolean}
* [bSuppressInvalidate] if true, the managed objects invalidate method is not called
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.setAssociation = function(sAssociationName, sId, bSuppressInvalidate) {
if (sId instanceof ManagedObject) {
sId = sId.getId();
} else if (sId != null && typeof sId !== "string") {
jQuery.sap.assert(false, "setAssociation(): sId must be a string, an instance of sap.ui.base.ManagedObject or null");
return this;
}
if (this.mAssociations[sAssociationName] === sId) {
return this;
} // no change
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
this.mAssociations[sAssociationName] = sId;
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
/**
* Returns the content of the association wit hthe given name.
*
* For associations of cardinality 0..1, a single string with the ID of an associated
* object is returned (if any). For cardinality 0..n, an array with the IDs of the
* associated objects is returned.
*
* If the association does not contain any objects(s), the given <code>oDefaultForCreation</code>
* is set as new value of the association and returned to the caller. The only supported values for
* <code>oDefaultForCreation</code> are <code>null</code> and <code>undefined</code> in the case of
* cardinality 0..1 and <code>null</code>, <code>undefined</code> or an empty array (<code>[]</code>)
* in case of cardinality 0..n. If the argument is omitted, <code>null</code> is used independently
* from the cardinality.
*
* <b>Note:</b> the need to specify a default value and the fact that it is stored as
* new value of a so far empty association is recognized as a shortcoming of this API
* but can no longer be changed for compatibility reasons.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically retrieve the content of an association.
* Use the concrete method get<i>XYZ</i> for association 'XYZ' instead.
*
* @param {string} sAssociationName the name of the association
* @param {object}
* oDefaultForCreation the object that is used in case the current aggregation is empty (only null or empty array allowed)
* @return {string | string[]} the ID of the associated managed object or an array of such IDs; may be null if the association has not been populated
* @protected
*/
ManagedObject.prototype.getAssociation = function(sAssociationName, oDefaultForCreation) {
var result = this.mAssociations[sAssociationName];
if (!result) {
result = this.mAssociations[sAssociationName] = oDefaultForCreation || null;
} else {
if (typeof result.length === 'number' && !(result.propertyIsEnumerable('length')) ) {
// Return a copy of the array instead of the array itself as reference!!
return result.slice();
}
// simple type or ManagedObject
return result;
}
return result;
};
/**
* Adds some object with the ID <code>sId</code> to the association identified by <code>sAssociationName</code> and
* marks this ManagedObject as changed.
*
* This method does not avoid duplicates.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically add an object to an association.
* Use the concrete method add<i>XYZ</i> for association 'XYZ' or the generic {@link #applySettings} instead.
*
* @param {string}
* sAssociationName the string identifying the association the object should be added to.
* @param {string | sap.ui.base.ManagedObject}
* sId the ID of the ManagedObject object to add; if empty, nothing is added; if a <code>sap.ui.base.ManagedObject</code> is given, its ID is added
* @param {boolean}
* [bSuppressInvalidate] if true, this managed object as well as the newly associated object are not marked as changed
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.addAssociation = function(sAssociationName, sId, bSuppressInvalidate) {
if (sId instanceof ManagedObject) {
sId = sId.getId();
} else if (typeof sId !== "string") {
// TODO what about empty string?
jQuery.sap.assert(false, "addAssociation(): sId must be a string or an instance of sap.ui.base.ManagedObject");
return this;
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
var aIds = this.mAssociations[sAssociationName];
if (!aIds) {
aIds = this.mAssociations[sAssociationName] = [sId];
} else {
aIds.push(sId);
}
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
/**
* Removes a ManagedObject from the association named <code>sAssociationName</code>.
*
* If an object is removed, the Id of that object is returned and this ManagedObject is
* marked as changed. Otherwise <code>undefined</code> is returned.
*
* If the same object was added multiple times to the same association, only a single
* occurence of it will be removed by this method. If the object is not found or if the
* parameter can't be interpreted neither as a ManagedObject (or id) nor as an index in
* the assocation, nothing will be removed. The same is true if an index is given and if
* that index is out of range for the association.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically remove an object from an association.
* Use the concrete method remove<i>XYZ</i> for association 'XYZ' instead.
*
* @param {string}
* sAssociationName the string identifying the association the ManagedObject should be removed from.
* @param {int | string | sap.ui.base.ManagedObject}
* vObject the position or ID of the ManagedObject to remove or the ManagedObject itself; if <code>vObject</code> is invalid input,
* a negative value or a value greater or equal than the current size of the association, nothing is removed
* @param {boolean}
* [bSuppressInvalidate] if true, the managed object is not marked as changed
* @return the ID of the removed ManagedObject or null
* @protected
*/
ManagedObject.prototype.removeAssociation = function(sAssociationName, vObject, bSuppressInvalidate) {
var aIds = this.mAssociations[sAssociationName];
var sId = null;
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
if (typeof (vObject) == "object" && vObject.getId) { // object itself is given
vObject = vObject.getId();
}
if (typeof (vObject) == "string") { // ID of the object is given or has just been retrieved
for (var i = 0; i < aIds.length; i++) {
if (aIds[i] == vObject) {
vObject = i;
break;
}
}
}
if (typeof (vObject) == "number") { // "object" is the index now
if (vObject < 0 || vObject >= aIds.length) {
jQuery.sap.log.warning("ManagedObject.removeAssociation called with invalid index: " + sAssociationName + ", " + vObject);
} else {
sId = aIds[vObject];
aIds.splice(vObject, 1);
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
}
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return sId;
};
/**
* Removes all the objects in the 0..n-association named <code>sAssociationName</code> and returns an array
* with their IDs. This ManagedObject is marked as changed, if the association contained any objects.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically remove all object from an association.
* Use the concrete method removeAll<i>XYZ</i> for association 'XYZ' instead.
*
* @param {string}
* sAssociationName the name of the association
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @type Array
* @return an array with the IDs of the removed objects (might be empty)
* @protected
*/
ManagedObject.prototype.removeAllAssociation = function(sAssociationName, bSuppressInvalidate){
var aIds = this.mAssociations[sAssociationName];
if (!aIds) {
return [];
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
delete this.mAssociations[sAssociationName];
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return aIds;
};
// ######################################################################################################
// Aggregations
// ######################################################################################################
/**
* Checks whether the given value is of the proper type for the given aggregation name.
*
* This method is already called by {@link #setAggregation}, {@link #addAggregation} and {@link #insertAggregation}.
* In many cases, subclasses of ManagedObject don't need to call it again in their mutator methods.
*
* @param {string} sAggregationName the name of the aggregation
* @param {sap.ui.base.ManagedObject|any} oObject the aggregated object or a primitive value
* @param {boolean} bMultiple whether the caller assumes the aggregation to have cardinality 0..n
* @return {sap.ui.base.ManagedObject|any} the passed object
* @throws Error if no aggregation with the given name is found or the given value does not fit to the aggregation type
* @protected
*/
ManagedObject.prototype.validateAggregation = function(sAggregationName, oObject, bMultiple) {
var oMetadata = this.getMetadata(),
oAggregation = oMetadata.getManagedAggregation(sAggregationName), // public or private
aAltTypes,
oType,
i,
msg;
// undeclared aggregation
if (!oAggregation) {
if (sAggregationName && oMetadata._mHiddenAggregations && oMetadata._mHiddenAggregations[sAggregationName]) {
oAggregation = oMetadata._mHiddenAggregations[sAggregationName];
jQuery.sap.log.error("Support for '_mHiddenAggregations' is about to be removed (with 1.12 latest). Hidden aggregations like '" + oMetadata.getName() + "." + sAggregationName + "' instead can be declared like normal aggregations but with visibility:'hidden'.");
} else {
msg = "Aggregation \"" + sAggregationName + "\" does not exist in " + this;
if ( /^sap\.(ui\.core|ui\.commons|ui\.table|ui\.ux3|m|makit|viz|uiext\.inbox)$/.test(oMetadata.getLibraryName() || "") ) {
throw new Error(msg);
} else {
// TODO throw for any lib as soon as "hidden" aggregations are a public feature.
// Otherwise, composite controls currently would have no legal way to react
jQuery.sap.log.error("Support for undeclared aggregations is about to be removed (with 1.12 latest). Hidden aggregations like '" + oMetadata.getName() + "." + sAggregationName + "' can be declared like normal aggregations but with visibility:'hidden'.");
jQuery.sap.assert(false, msg);
return oObject;
}
}
}
if (oAggregation.multiple !== bMultiple ) {
throw new Error("Aggregation '" + sAggregationName + "' of " + this + " used with wrong cardinality (declared as " + (oAggregation.multiple ? "0..n" : "0..1") + ")");
}
//Null is a valid value for 0..1 aggregations
if (!oAggregation.multiple && !oObject) {
return oObject;
}
oType = jQuery.sap.getObject(oAggregation.type);
// class types
if ( typeof oType === "function" && oObject instanceof oType ) {
return oObject;
}
// interfaces
if ( oObject && oObject.getMetadata && oObject.getMetadata().isInstanceOf(oAggregation.type) ) {
return oObject;
}
// alternative types
aAltTypes = oAggregation.altTypes;
if ( aAltTypes && aAltTypes.length ) {
// for primitive types, null or undefined is valid as well
if ( oObject == null ) {
return oObject;
}
for (i = 0; i < aAltTypes.length; i++) {
oType = DataType.getType(aAltTypes[i]);
if (oType instanceof DataType) {
if (oType.isValid(oObject)) {
return oObject;
}
} else if (oObject in oType) { // Enumeration
return oObject;
}
}
}
// TODO make this stronger again (e.g. for FormattedText)
msg = "\"" + oObject + "\" is not valid for aggregation \"" + sAggregationName + "\" of " + this;
if ( DataType.isInterfaceType(oAggregation.type) ) {
jQuery.sap.assert(false, msg);
return oObject;
} else {
throw new Error(msg);
}
};
/**
* Sets a new object in the named 0..1 aggregation of this ManagedObject and
* marks this ManagedObject as changed.
*
* If the given object is not valid with regard to the aggregation (if it is not an instance
* of the type specified for that aggregation) or when the method is called for an aggregation
* of cardinality 0..n, then an Error is thrown (see {@link #validateAggregation}.
*
* If the new object is the same as the currently aggregated object, then the internal state
* is not modified and this ManagedObject is not marked as changed.
*
* If the given object is different, the parent of a previously aggregated object is cleared
* (it must have been this ManagedObject before), the parent of the given object is set to this
* ManagedObject and {@link #invalidate} is called for this object.
*
* Note that this method does neither return nor destroy the previously aggregated object.
* This behavior is inherited by named set methods (see below) in subclasses.
* To avoid memory leaks, applications therefore should first get the aggregated object,
* keep a reference to it or destroy it, depending on their needs, and only then set a new
* object.
*
* Note that ManagedObject only implements a single level of change tracking: if a first
* call to setAggregation recognizes a change, 'invalidate' is called. If another call to
* setAggregation reverts that change, invalidate() will be called again, the new status
* is not recognized as being 'clean' again.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically set an object in an aggregation.
* Use the concrete method set<i>XYZ</i> for aggregation 'XYZ' or the generic {@link #applySettings} instead.
*
* @param {string}
* sAggregationName name of an 0..1 aggregation
* @param {object}
* oObject the managed object that is set as aggregated object
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @throws {Error}
* @protected
*/
ManagedObject.prototype.setAggregation = function(sAggregationName, oObject, bSuppressInvalidate) {
var oOldChild = this.mAggregations[sAggregationName];
if (oOldChild === oObject) {
return this;
} // no change
oObject = this.validateAggregation(sAggregationName, oObject, /* multiple */ false);
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
if (oOldChild instanceof ManagedObject) { // remove old child
oOldChild.setParent(null);
}
this.mAggregations[sAggregationName] = oObject;
if (oObject instanceof ManagedObject) { // adopt new child
oObject.setParent(this, sAggregationName, bSuppressInvalidate);
} else {
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
/**
* Returns the aggregated object(s) for the named aggregation of this ManagedObject.
*
* If the aggregation does not contain any objects(s), the given <code>oDefaultForCreation</code>
* (or <code>null</code>) is set as new value of the aggregation and returned to the caller.
*
* <b>Note:</b> the need to specify a default value and the fact that it is stored as
* new value of a so far empty aggregation is recognized as a shortcoming of this API
* but can no longer be changed for compatibility reasons.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically read the content of an aggregation.
* Use the concrete method get<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the name of the aggregation
* @param {sap.ui.base.ManagedObject | Array}
* oDefaultForCreation the object that is used in case the current aggregation is empty
* @type sap.ui.base.ManagedObject|Array
* @return the aggregation array in case of 0..n-aggregations or the managed object or null in case of 0..1-aggregations
* @protected
*/
ManagedObject.prototype.getAggregation = function(sAggregationName, oDefaultForCreation) {
var aChildren = this.mAggregations[sAggregationName];
if (!aChildren) {
aChildren = this.mAggregations[sAggregationName] = oDefaultForCreation || null;
}
if (aChildren) {
if (typeof aChildren.length === 'number' && !(aChildren.propertyIsEnumerable('length')) ) {
// Return a copy of the array instead of the array itself as reference!!
return aChildren.slice();
}
// simple type or ManagedObject
return aChildren;
} else {
return null;
}
};
/**
* Searches for the provided ManagedObject in the named aggregation and returns its
* 0-based index if found, or -1 otherwise. Returns -2 if the given named aggregation
* is of cardinality 0..1 and doesn't reference the given object.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically determine the position of an object in an aggregation.
* Use the concrete method indexOf<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the name of the aggregation
* @param {sap.ui.base.ManagedObject}
* oObject the ManagedObject whose index is looked for.
* @return {int} the index of the provided managed object in the aggregation.
* @protected
*/
ManagedObject.prototype.indexOfAggregation = function(sAggregationName, oObject) {
var aChildren = this.mAggregations[sAggregationName];
if (aChildren) {
if (aChildren.length == undefined) {
return -2;
} // not a multiple aggregation
for (var i = 0; i < aChildren.length; i++) {
if (aChildren[i] == oObject) {
return i;
}
}
}
return -1;
};
/**
* Inserts managed object <code>oObject</code> to the aggregation named <code>sAggregationName</code> at
* position <code>iIndex</code>.
*
* If the given object is not valid with regard to the aggregation (if it is not an instance
* of the type specified for that aggregation) or when the method is called for an aggregation
* of cardinality 0..1, then an Error is thrown (see {@link #validateAggregation}.
*
* If the given index is out of range with respect to the current content of the aggregation,
* it is clipped to that range (0 for iIndex < 0, n for iIndex > n).
*
* Please note that this method does not work as expected when an object is added
* that is already part of the aggregation. In order to change the index of an object
* inside an aggregation, first remove it, then insert it again.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically insert an object into an aggregation.
* Use the concrete method insert<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the string identifying the aggregation the managed object <code>oObject</code>
* should be inserted into.
* @param {sap.ui.base.ManagedObject}
* oObject the ManagedObject to add; if empty, nothing is inserted.
* @param {int}
* iIndex the <code>0</code>-based index the managed object should be inserted at; for a negative
* value <code>iIndex</code>, <code>oObject</code> is inserted at position 0; for a value
* greater than the current size of the aggregation, <code>oObject</code> is inserted at
* the last position
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject as well as the added child are not marked as changed
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.insertAggregation = function(sAggregationName, oObject, iIndex, bSuppressInvalidate) {
if (!oObject) {
return this;
}
oObject = this.validateAggregation(sAggregationName, oObject, /* multiple */ true);
var aChildren = this.mAggregations[sAggregationName] || (this.mAggregations[sAggregationName] = []);
// force index into valid range
var i;
if (iIndex < 0) {
i = 0;
} else if (iIndex > aChildren.length) {
i = aChildren.length;
} else {
i = iIndex;
}
if (i !== iIndex) {
jQuery.sap.log.warning("ManagedObject.insertAggregation: index '" + iIndex + "' out of range [0," + aChildren.length + "], forced to " + i);
}
aChildren.splice(i, 0, oObject);
oObject.setParent(this, sAggregationName, bSuppressInvalidate);
return this;
};
/**
* Adds some entity <code>oObject</code> to the aggregation identified by <code>sAggregationName</code>.
*
* If the given object is not valid with regard to the aggregation (if it is not an instance
* of the type specified for that aggregation) or when the method is called for an aggregation
* of cardinality 0..1, then an Error is thrown (see {@link #validateAggregation}.
*
* If the aggregation already has content, the new object will be added after the current content.
* If the new object was already contained in the aggregation, it will be moved to the end.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically add an object to an aggregation.
* Use the concrete method add<i>XYZ</i> for aggregation 'XYZ' or the generic {@link #applySettings} instead.
*
* @param {string}
* sAggregationName the string identifying the aggregation that <code>oObject</code> should be added to.
* @param {sap.ui.base.ManagedObject}
* oObject the object to add; if empty, nothing is added
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject as well as the added child are not marked as changed
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.addAggregation = function(sAggregationName, oObject, bSuppressInvalidate) {
if (!oObject) {
return this;
}
oObject = this.validateAggregation(sAggregationName, oObject, /* multiple */ true);
var aChildren = this.mAggregations[sAggregationName];
if (!aChildren) {
aChildren = this.mAggregations[sAggregationName] = [oObject];
} else {
aChildren.push(oObject);
}
oObject.setParent(this, sAggregationName, bSuppressInvalidate);
return this;
};
/**
* Removes an object from the aggregation named <code>sAggregationName</code> with cardinality 0..n.
*
* The removed object is not destroyed nor is it marked as changed.
*
* If the given object is found in the aggreation, it is removed, it's parent relationship
* is unset and this ManagedObject is marked as changed. The removed object is returned as
* result of this method. If the object could not be found, <code>undefined</code> is returned.
*
* This method must only be called for aggregations of cardinality 0..n. The only way to remove objects
* from a 0..1 aggregation is to set a <code>null</code> value for them.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically remove an object from an aggregation.
* Use the concrete method remove<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the string identifying the aggregation that the given object should be removed from
* @param {int | string | sap.ui.base.ManagedObject}
* vObject the position or ID of the ManagedObject that should be removed or that ManagedObject itself;
* if <code>vObject</code> is invalid, a negative value or a value greater or equal than the current size
* of the aggregation, nothing is removed.
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @return {sap.ui.base.ManagedObject} the removed object or null
* @protected
*/
ManagedObject.prototype.removeAggregation = function(sAggregationName, vObject, bSuppressInvalidate) {
var aChildren = this.mAggregations[sAggregationName],
oChild = null,
i;
if ( !aChildren ) {
return null;
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
if (typeof (vObject) == "string") { // ID of the object is given
// Note: old lookup via sap.ui.getCore().byId(vObject) only worked for Elements, not for managed objects in general!
for (i = 0; i < aChildren.length; i++) {
if (aChildren[i] && aChildren[i].getId() === vObject) {
vObject = i;
break;
}
}
}
if (typeof (vObject) == "object") { // the object itself is given or has just been retrieved
for (i = 0; i < aChildren.length; i++) {
if (aChildren[i] == vObject) {
vObject = i;
break;
}
}
}
if (typeof (vObject) == "number") { // "vObject" is the index now
if (vObject < 0 || vObject >= aChildren.length) {
jQuery.sap.log.warning("ManagedObject.removeAggregation called with invalid index: " + sAggregationName + ", " + vObject);
} else {
oChild = aChildren[vObject];
aChildren.splice(vObject, 1); // first remove it from array, then call setParent (avoids endless recursion)
oChild.setParent(null);
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
}
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return oChild;
};
/**
* Removes all objects from the 0..n-aggregation named <code>sAggregationName</code>.
*
* The removed objects are not destroyed nor are they marked as changed.
*
* Additionally, it clears the parent relationship of all removed objects, marks this
* ManagedObject as changed and returns an array with the removed objects.
*
* If the aggregation did not contain any objects, an empty array is returned and this
* ManagedObject is not marked as changed.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically remove all objects from an aggregation.
* Use the concrete method removeAll<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the name of the aggregation
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @type Array
* @return an array of the removed elements (might be empty)
* @protected
*/
ManagedObject.prototype.removeAllAggregation = function(sAggregationName, bSuppressInvalidate){
var aChildren = this.mAggregations[sAggregationName];
if (!aChildren) {
return [];
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
delete this.mAggregations[sAggregationName];
for (var i = 0; i < aChildren.length; i++) {
aChildren[i].setParent(null);
}
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return aChildren;
};
/**
* Destroys (all) the managed object(s) in the aggregation named <code>sAggregationName</code> and empties the
* aggregation. If the aggregation did contain any object, this ManagedObject is marked as changed.
*
* <b>Note:</b> This method is a low-level API as described in <a href="#lowlevelapi">the class documentation</a>.
* Applications or frameworks must not use this method to generically destroy all objects in an aggregation.
* Use the concrete method destroy<i>XYZ</i> for aggregation 'XYZ' instead.
*
* @param {string}
* sAggregationName the name of the aggregation
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @return {sap.ui.base.ManagedObject} Returns <code>this</code> to allow method chaining
* @protected
*/
ManagedObject.prototype.destroyAggregation = function(sAggregationName, bSuppressInvalidate){
var aChildren = this.mAggregations[sAggregationName],
i, aChild;
if (!aChildren) {
return this;
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
// Deleting the aggregation here before destroying the children is a BUG:
//
// The destroy() method on the children calls _removeChild() on this instance
// to properly remove each child from the bookkeeping by executing the named
// removeXYZ() method. But as the aggegation is deleted here already,
// _removeChild() doesn't find the child in the bookkeeping and therefore
// refuses to work. As a result, side effects from removeXYZ() are missing.
//
// The lines below marked with 'FIXME DESTROY' sketch a potential fix, but
// that fix has proven to be incompatible for several controls that don't
// properly implement removeXYZ(). As this might affect custom controls
// as well, the fix has been abandoned.
//
delete this.mAggregations[sAggregationName]; //FIXME DESTROY: should be removed here
if (aChildren instanceof ManagedObject) {
// FIXME DESTROY: this._removeChild(aChildren, sAggregationName, bSuppressInvalidate); // (optional, done by destroy())
aChildren.destroy(bSuppressInvalidate);
} else if (jQuery.isArray(aChildren)) {
for (i = aChildren.length - 1; i >= 0; i--) {
aChild = aChildren[i];
if (aChild) {
// FIXME DESTROY: this._removeChild(aChild, sAggregationName, bSuppressInvalidate); // (optional, done by destroy())
aChild.destroy(bSuppressInvalidate);
}
}
}
// FIXME DESTROY: // 'delete' aggregation only now so that _removeChild() can still do its cleanup
// FIXME DESTROY: delete this.mAggregations[sAggregationName];
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
// ######################################################################################################
// End of Aggregations
// ######################################################################################################
/**
* This triggers rerendering of itself and its children.<br/> As <code>sap.ui.base.ManagedObject</code> "bubbles up" the
* invalidate, changes to child-<code>Elements</code> will also result in rerendering of the whole sub tree.
* @protected
*/
ManagedObject.prototype.invalidate = function() {
if (this.oParent) {
this.oParent.invalidate(this);
}
};
/**
* Returns whether rerendering is currently suppressed on this ManagedObject
* @return boolean
* @protected
*/
ManagedObject.prototype.isInvalidateSuppressed = function() {
var bInvalidateSuppressed = this.iSuppressInvalidate > 0;
if (this.oParent && this.oParent instanceof ManagedObject) {
bInvalidateSuppressed = bInvalidateSuppressed || this.oParent.isInvalidateSuppressed();
}
return bInvalidateSuppressed;
};
/**
* Removes the given child from this object's named aggregation.
* @see sap.ui.core.UIArea#_removeChild
* @see sap.ui.base.ManagedObject#setParent
*
* @param {sap.ui.base.ManagedObject}
* oChild the child object to be removed
* @param {string}
* sAggregationName the name of this object's aggregation
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @private
*/
ManagedObject.prototype._removeChild = function(oChild, sAggregationName, bSuppressInvalidate) {
if (!sAggregationName) {
// an aggregation name has to be specified!
jQuery.sap.log.error("Cannot remove aggregated child without aggregation name.", null, this);
} else {
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
var iIndex = this.indexOfAggregation(sAggregationName, oChild);
var oAggregationInfo = this.getMetadata().getAggregation(sAggregationName);
// Note: we assume that this is the given child's parent, i.e. -1 not expected!
if (iIndex == -2) { // 0..1
if (oAggregationInfo && this[oAggregationInfo._sMutator]) { // TODO properly deal with hidden aggregations
this[oAggregationInfo._sMutator](null);
} else {
this.setAggregation(sAggregationName, null, bSuppressInvalidate);
}
} else if (iIndex > -1 ) { // 0..n
if (oAggregationInfo && this[oAggregationInfo._sRemoveMutator]) { // TODO properly deal with hidden aggregations
this[oAggregationInfo._sRemoveMutator](iIndex);
} else {
this.removeAggregation(sAggregationName, iIndex, bSuppressInvalidate);
}
} //else {
// already removed!?
// this is the unexpected -1
// TODO: What would be better? Explicit removeCompositeChild callback on subclass?
//}
if (!this.isInvalidateSuppressed()) {
this.invalidate();
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
}
};
/**
* Defines this object's new parent. If no new parent is given, the parent is
* just unset and we assume that the old parent has removed this child from its
* aggregation. But if a new parent is given, this child is first removed from
* its old parent.
*
* @param {sap.ui.base.ManagedObject}
* oParent the object that becomes this objects's new parent
* @param {string}
* sAggregationName the name of the parent objects's aggregation
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed. The old parent, however, is marked.
* @return {sap.ui.base.ManagedObject}
* Returns <code>this</code> to allow method chaining
* @private
*/
ManagedObject.prototype.setParent = function(oParent, sAggregationName, bSuppressInvalidate) {
if ( !oParent ) {
this.oParent = null;
this.sParentAggregationName = null;
this.oPropagatedProperties = {oModels:{}, oBindingContexts:{}};
jQuery.sap.act.refresh();
// Note: no need (and no way how) to invalidate
return;
}
// set suppress invalidate flag
if (bSuppressInvalidate) {
//Refresh only for changes with suppressed invalidation (others lead to rerendering and refresh is handled there)
jQuery.sap.act.refresh();
this.iSuppressInvalidate++;
}
var oOldParent = this.getParent();
if (oOldParent) { // remove this object from its old parent
// Note: bSuppressInvalidate by intention is not propagated to the old parent.
// It is not sure whether the (direct or indirect) caller of setParent
// has enough knowledge about the old parent to automatically propagate this.
// If needed, callers can first remove the object from the oldParent (specifying a
// suitable value for bSuppressInvalidate there) and only then call setParent.
oOldParent._removeChild(this, this.sParentAggregationName);
}
// adopt new parent
this.oParent = oParent;
this.sParentAggregationName = sAggregationName;
//get properties to propagate
this.oPropagatedProperties = oParent._getPropertiesToPropagate();
// update bindings
if (this.hasModel()) {
this.updateBindingContext(false, true, undefined, true);
this.updateBindings(true,null); // TODO could be restricted to models that changed
this.propagateProperties(true);
}
// only the parent knows where to render us, so we have to invalidate it
if ( oParent && !this.isInvalidateSuppressed() ) {
oParent.invalidate(this);
}
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
return this;
};
/**
* Returns the parent managed object or <code>null</code> if this object hasn't been added to a parent yet.
*
* @return {sap.ui.base.ManagedObject} The parent managed object or <code>null</code>
* @public
*/
ManagedObject.prototype.getParent = function() {
/* Be aware that internally this.oParent is used to reduce method calls.
* Check for side effects when overriding this method */
return this.oParent;
};
/**
* Cleans up the resources associated with this object and all its aggregated children.
*
* After an object has been destroyed, it can no longer be used in!
*
* Applications should call this method if they don't need the object any longer.
*
* @param {boolean}
* [bSuppressInvalidate] if true, this ManagedObject is not marked as changed
* @public
*/
ManagedObject.prototype.destroy = function(bSuppressInvalidate) {
var that = this;
// set suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate++;
}
if (this.exit) {
this.exit();
}
// TODO: generic concept for exit hooks?
if ( this._exitCompositeSupport ) {
this._exitCompositeSupport();
}
// ensure that also our children are destroyed!!
for (var oAggr in this.mAggregations) {
this.destroyAggregation(oAggr, bSuppressInvalidate);
}
// Deregister, if available
if (this.deregister) {
this.deregister();
}
// remove this child from parent aggregation
if (this.oParent && this.sParentAggregationName) {
this.oParent._removeChild(this, this.sParentAggregationName, bSuppressInvalidate);
}
// for robustness only - should have been cleared by _removeChild already
delete this.oParent;
// Data Binding
jQuery.each(this.mBindingInfos, function(sName, oBindingInfo) {
if (oBindingInfo.factory) {
that.unbindAggregation(sName, true);
} else {
that.unbindProperty(sName, true);
}
});
// reset suppress invalidate flag
if (bSuppressInvalidate) {
this.iSuppressInvalidate--;
}
sap.ui.getCore().getMessageManager().removeMessages(this._aMessages);
this._aMessages = undefined;
EventProvider.prototype.destroy.apply(this, arguments);
// finally make the object unusable
this.setParent = function(){
throw Error("The object with ID " + that.getId() + " was destroyed and cannot be used anymore.");
};
// make visible that it's been destroyed.
this.bIsDestroyed = true;
};
// DataBinding
/**
* Binding parser to use.
*/
ManagedObject.bindingParser = BindingParser.simpleParser;
/**
* Determines whether a given object contains binding information instead of a
* value or aggregated controls. The method is used in applySettings for processing
* the JSON notation of properties/aggregations in the constructor.
*
* @param {object} oValue the value
* @param {object} oKeyInfo the metadata of the property
*
* @returns {boolean} whether the value contains binding information
*
* @private
* @deprecated
*/
ManagedObject.prototype.isBinding = function(oValue, oKeyInfo) {
return typeof this.extractBindingInfo(oValue) === "object";
};
/**
* Checks whether the given value can be interpreted as a binding info and
* returns that binding info or an unescaped string or undefined when it is not.
*
* When the 'complex' binding syntax is enabled, the function might also return
* a string value in case the given value was a string, did not represent a binding
* but contained escaped special characters.
*
* There are two possible notations for binding information in the object literal notation
* of the ManagedObject constructor and ManagedObject.applySettings:
* <ul>
* <li>property: "{path}"
* This is used for property binding and can only contain the path.
* </li>
* <li>property:{path:"path", template:oTemplate}
* This is used for aggregation binding, where a template is required or can
* be used for property binding when additional data is required (e.g. formatter).
* </li>
* </ul>
*
* @param {object} oValue
* @param {object} oScope
*
* @returns {object} the binding info object or an unescaped string or undefined.
* If a binding info is returned, it contains at least a path property
* or nested bindings (parts) and, dependant of the binding type,
* additional properties
*
* @private
*/
ManagedObject.prototype.extractBindingInfo = function(oValue, oScope) {
// property:{path:"path", template:oTemplate}
if (oValue && typeof oValue === "object") {
if (oValue.ui5object) {
// if value contains ui5object property, this is not a binding info,
// remove it and not check for path or parts property
delete oValue.ui5object;
} else if (oValue.path || oValue.parts) {
// allow JSON syntax for templates
if (oValue.template) {
oValue.template = ManagedObject.create(oValue.template);
}
return oValue;
}
}
// property:"{path}" or "\{path\}"
if (typeof oValue === "string") {
// either returns a binding info or an unescaped string or undefined - depending on binding syntax
return ManagedObject.bindingParser(oValue, oScope, true);
}
// return undefined;
};
/**
* Returns the binding infos for the given property or aggregation. The binding info contains information about path, binding object, format options,
* sorter, filter etc. for the property or aggregation.
*
* @param {string} sName the name of the property or aggregation
*
* @returns {object} the binding info object, containing at least a path property
* and, dependant of the binding type, additional properties
*
* @protected
*/
ManagedObject.prototype.getBindingInfo = function(sName) {
return this.mBindingInfos[sName];
};
/**
* Bind the object to the referenced entity in the model, which is used as the binding context
* to resolve bound properties or aggregations of the object itself and all of its children
* relatively to the given path.
* If a relative binding path is used, this will be applied whenever the parent context changes.
* @param {string} sPath the binding path
* @param {object} [mParameters] map of additional parameters for this binding
*
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.bindObject = function(sPath, mParameters) {
var boundObject = {},
oldBoundObject,
sModelName,
iSeparatorPos;
// support object notation
if (typeof sPath == "object") {
var oBindingInfo = sPath;
sPath = oBindingInfo.path;
mParameters = oBindingInfo.parameters;
sModelName = oBindingInfo.model;
boundObject.events = oBindingInfo.events;
}
// if a model separator is found in the path, extract model name and path
iSeparatorPos = sPath.indexOf(">");
boundObject.sBindingPath = sPath;
boundObject.mBindingParameters = mParameters;
if (iSeparatorPos > 0) {
sModelName = sPath.substr(0, iSeparatorPos);
boundObject.sBindingPath = sPath.substr(iSeparatorPos + 1);
}
//if old binding exists detach handler
oldBoundObject = this.mBoundObjects[sModelName];
if (oldBoundObject && oldBoundObject.binding) {
oldBoundObject.binding.detachChange(oldBoundObject.fChangeHandler);
oldBoundObject.binding.detachEvents(oldBoundObject.events);
//clear elementContext
delete this.mElementBindingContexts[sModelName];
}
this.mBoundObjects[sModelName] = boundObject;
// if the models are already available, create the binding
if (this.getModel(sModelName)) {
this._bindObject(sModelName, boundObject);
}
return this;
};
/**
* Create object binding
*
* @private
*/
ManagedObject.prototype._bindObject = function(sModelName, oBoundObject) {
var oBinding,
oContext,
oModel,
that = this;
var fChangeHandler = function(oEvent) {
/* as we reuse the context objects we need to ensure an update of relative bindings. Therefore we set
the context to null so relative bindings will detect a context change */
if (oBinding.getBoundContext() === that.getBindingContext(sModelName)) {
that.setElementBindingContext(null, sModelName);
}
that.setElementBindingContext(oBinding.getBoundContext(), sModelName);
};
oModel = this.getModel(sModelName);
oContext = this.getBindingContext(sModelName);
oBinding = oModel.bindContext(oBoundObject.sBindingPath, oContext, oBoundObject.mBindingParameters);
oBinding.attachChange(fChangeHandler);
oBoundObject.binding = oBinding;
oBoundObject.fChangeHandler = fChangeHandler;
oBinding.attachEvents(oBoundObject.events);
oBinding.initialize();
};
/**
* Bind the object to the referenced entity in the model, which is used as the binding context
* to resolve bound properties or aggregations of the object itself and all of its children
* relatively to the given path.
*
* @deprecated Since 1.11.1, please use bindElement instead.
* @param {string} sPath the binding path
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.bindContext = function(sPath) {
return this.bindElement(sPath);
};
/**
* Removes the defined binding context of this object, all bindings will now resolve
* relative to the parent context again.
*
* @deprecated Since 1.11.1, please use unbindElement instead.
* @param {string} [sModelName] name of the model to remove the context for.
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.unbindContext = function(sModelName) {
return this.unbindElement(sModelName);
};
/**
* Removes the defined binding context of this object, all bindings will now resolve
* relative to the parent context again.
*
* @param {string} [sModelName] name of the model to remove the context for.
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.unbindObject = function(sModelName) {
var oBoundObject = this.mBoundObjects[sModelName];
if (oBoundObject) {
if (oBoundObject.binding) {
oBoundObject.binding.detachChange(oBoundObject.fChangeHandler);
oBoundObject.binding.detachEvents(oBoundObject.events);
}
delete this.mBoundObjects[sModelName];
delete this.mElementBindingContexts[sModelName];
this.updateBindingContext(false, false, sModelName);
}
return this;
};
/**
* Bind a property to the model.
* The Setter for the given property will be called with the value retrieved
* from the data model.
* This is a generic method which can be used to bind any property to the
* model. A managed object may flag properties in the metamodel with
* bindable="bindable" to get typed bind methods for a property.
* A composite property binding which may have multiple paths (also known as Calculated Fields) can be declared using the parts parameter.
* Note a composite binding is read only (One Way).
*
* @param {string} sName the name of the property
* @param {object} oBindingInfo the binding information
* @param {string} oBindingInfo.path the binding path
* @param {string} [oBindingInfo.model] the model identifier
* @param {function} [oBindingInfo.formatter] the formatter function
* @param {boolean} [oBindingInfo.useRawValues] determines if the parameters in the formatter functions should be passed as raw values or not. In this case
* the specified type for the binding is not used and the values are not formatted. Note: use this flag only when using multiple bindings.
* If you use only one binding and want raw values then simply don't specify a type for that binding.
* @param {sap.ui.model.Type|string} [oBindingInfo.type] the sap.ui.model.Type object or class name
* @param {object} [oBindingInfo.formatOptions] the format options to be used
* @param {object} [oBindingInfo.constraints] the constraints for this value
* @param {sap.ui.model.BindingMode} [oBindingInfo.mode=Default] the binding mode to be used for this property binding (e.g. one way)
* @param {object} [oBindingInfo.parameters] a map of parameters which is passed to the binding
* @param {object} [oBindingInfo.parts] object for definding a read only composite binding which may have multiple binding paths also in different models.
* <code>oTxt.bindValue({
* parts: [
* {path: "/firstName", type: new sap.ui.model.type.String()},
* {path: "myModel2>/lastName"}
* ]
* }); </code>
*
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.bindProperty = function(sName, oBindingInfo) {
var sPath,
oFormat,
sMode,
fnFormatter,
oType,
iSeparatorPos,
bAvailable = true,
that = this,
oProperty = this.getMetadata().getPropertyLikeSetting(sName);
// check whether property or alternative type on aggregation exists
if (!oProperty) {
throw new Error("Property \"" + sName + "\" does not exist in " + this);
}
// old API compatbility (sName, sPath, oFormat, sMode)
if (typeof oBindingInfo == "string") {
sPath = arguments[1];
oFormat = arguments[2];
sMode = arguments[3];
// find out whether formatter or type has been provided
if (typeof oFormat == "function") {
fnFormatter = oFormat;
} else if (oFormat instanceof Type) {
oType = oFormat;
}
oBindingInfo = {formatter: fnFormatter, parts : [ {path: sPath, type: oType, mode: sMode} ]};
}
// only one binding object with one binding specified
if (!oBindingInfo.parts) {
oBindingInfo.parts = [];
oBindingInfo.parts[0] = {
path: oBindingInfo.path,
type: oBindingInfo.type,
formatOptions: oBindingInfo.formatOptions,
constraints: oBindingInfo.constraints,
model: oBindingInfo.model,
mode: oBindingInfo.mode
};
delete oBindingInfo.path;
delete oBindingInfo.mode;
delete oBindingInfo.model;
}
jQuery.each(oBindingInfo.parts, function(i, oPart) {
if (typeof oPart == "string") {
oPart = { path: oPart };
oBindingInfo.parts[i] = oPart;
}
// if a model separator is found in the path, extract model name and path
iSeparatorPos = oPart.path.indexOf(">");
if (iSeparatorPos > 0) {
oPart.model = oPart.path.substr(0, iSeparatorPos);
oPart.path = oPart.path.substr(iSeparatorPos + 1);
}
// if a formatter exists the binding mode can be one way only
if (oBindingInfo.formatter) {
oPart.mode = sap.ui.model.BindingMode.OneWay;
}
if (!that.getModel(oPart.model)) {
bAvailable = false;
}
});
// if property is already bound, unbind it first
if (this.isBound(sName)) {
this.unbindProperty(sName, true);
}
// store binding info to create the binding, as soon as the model is available, or when the model is changed
this.mBindingInfos[sName] = oBindingInfo;
// if the models are already available, create the binding
if (bAvailable) {
this._bindProperty(sName, oBindingInfo);
}
return this;
};
ManagedObject.prototype._bindProperty = function(sName, oBindingInfo) {
var oModel,
oContext,
oBinding,
sMode,
sCompositeMode = sap.ui.model.BindingMode.TwoWay,
oType,
clType,
oPropertyInfo = this.getMetadata().getPropertyLikeSetting(sName), // TODO fix handling of hidden entitites?
sInternalType = oPropertyInfo._iKind === /* PROPERTY */ 0 ? oPropertyInfo.type : oPropertyInfo.altTypes[0],
that = this,
aBindings = [],
fModelChangeHandler = function(oEvent){
var oMessageManager = sap.ui.getCore().getMessageManager();
that.updateProperty(sName);
//clear Messages from messageManager
if (oMessageManager && that._aMessages && that._aMessages.length > 0) {
sap.ui.getCore().getMessageManager().removeMessages(that._aMessages);
that._aMessages = [];
}
//delete control Messages (value is updated from model) and update control with model messages
if (oBinding.getMessages()) {
that.updateMessages(sName, oBinding.getMessages());
}
if (oBinding.getBindingMode() === sap.ui.model.BindingMode.OneTime) {
oBinding.detachChange(fModelChangeHandler);
oBinding.detachEvents(oBindingInfo.events);
}
},
fMessageChangeHandler = function(oEvent){
var aAllMessages = [];
var sMessageSource = oEvent.getParameter("messageSource");
var aMessages = oEvent.getParameter("messages");
if (sMessageSource == "control") {
that._aMessages = aMessages;
}
//merge object/model messages
if (that._aMessages && that._aMessages.length > 0) {
aAllMessages = aAllMessages.concat(that._aMessages);
}
if (oBinding.getMessages()) {
aAllMessages = aAllMessages.concat(oBinding.getMessages());
}
that.updateMessages(sName, aAllMessages);
};
// Only use context for bindings on the primary model
oContext = this.getBindingContext(oBindingInfo.model);
jQuery.each(oBindingInfo.parts, function(i, oPart) {
// Only use context for bindings on the primary model
oContext = that.getBindingContext(oPart.model);
// Create binding object
oModel = that.getModel(oPart.model);
// Create type instance if needed
oType = oPart.type;
if (typeof oType == "string") {
clType = jQuery.sap.getObject(oType);
oType = new clType(oPart.formatOptions, oPart.constraints);
}
oBinding = oModel.bindProperty(oPart.path, oContext, oBindingInfo.parameters);
oBinding.setType(oType, sInternalType);
oBinding.setFormatter(oPart.formatter);
sMode = oPart.mode || oModel.getDefaultBindingMode();
oBinding.setBindingMode(sMode);
// Only if all parts have twoway binding enabled, the composite binding will also have twoway binding
if (sMode != sap.ui.model.BindingMode.TwoWay) {
sCompositeMode = sap.ui.model.BindingMode.OneWay;
}
aBindings.push(oBinding);
});
// check if we have a composite binding or a formatter function created by the BindingParser which has property textFragments
if (aBindings.length > 1 || ( oBindingInfo.formatter && oBindingInfo.formatter.textFragments )) {
// Create type instance if needed
oType = oBindingInfo.type;
if (typeof oType == "string") {
clType = jQuery.sap.getObject(oType);
oType = new clType(oBindingInfo.formatOptions, oBindingInfo.constraints);
}
oBinding = new CompositeBinding(aBindings, oBindingInfo.useRawValues);
oBinding.setType(oType, sInternalType);
oBinding.setBindingMode(oBindingInfo.mode || sCompositeMode);
} else {
oBinding = aBindings[0];
}
oBinding.attachChange(fModelChangeHandler);
oBinding.attachMessageChange(fMessageChangeHandler);
// set only one formatter function if any
// because the formatter gets the context of the element we have to set the context via proxy to ensure compatibility
// for formatter function which is now called by the property binding
// proxy formatter here because "this" is the correct cloned object
oBinding.setFormatter(jQuery.proxy(oBindingInfo.formatter, this));
// Set additional information on the binding info
oBindingInfo.binding = oBinding;
oBindingInfo.modelChangeHandler = fModelChangeHandler;
oBinding.attachEvents(oBindingInfo.events);
oBinding.initialize();
};
/**
* Unbind the property from the model
*
* @param {string} sName the name of the property
* @param {boolean} bSuppressReset whether the reset to the default value when unbinding should be suppressed
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.unbindProperty = function(sName, bSuppressReset){
var oBindingInfo = this.mBindingInfos[sName],
oPropertyInfo = this.getMetadata().getPropertyLikeSetting(sName);
if (oBindingInfo) {
if (oBindingInfo.binding) {
oBindingInfo.binding.detachChange(oBindingInfo.modelChangeHandler);
oBindingInfo.binding.detachEvents(oBindingInfo.events);
}
delete this.mBindingInfos[sName];
if (!bSuppressReset) {
this[oPropertyInfo._sMutator](null);
}
}
return this;
};
/**
* Generic method which is called, whenever an property binding is changed.
* This method gets the external format from the property binding and applies
* it to the setter.
*
* @private
*/
ManagedObject.prototype.updateProperty = function(sName) {
var oBindingInfo = this.mBindingInfos[sName],
oBinding = oBindingInfo.binding,
oPropertyInfo = this.getMetadata().getPropertyLikeSetting(sName);
// If model change was triggered by the property itself, don't call the setter again
if (oBindingInfo.skipPropertyUpdate) {
return;
}
try {
var oValue = oBinding.getExternalValue();
oBindingInfo.skipModelUpdate = true;
this[oPropertyInfo._sMutator](oValue);
oBindingInfo.skipModelUpdate = false;
} catch (oException) {
oBindingInfo.skipModelUpdate = false;
if (oException instanceof sap.ui.model.FormatException) {
this.fireFormatError({
element : this,
property : sName,
type : oBinding.getType(),
newValue : oBinding.getValue(),
oldValue : this[oPropertyInfo._sGetter](),
exception: oException,
message: oException.message
}, false, true); // bAllowPreventDefault, bEnableEventBubbling
oBindingInfo.skipModelUpdate = true;
this[oPropertyInfo._sMutator](null);
oBindingInfo.skipModelUpdate = false;
} else {
throw oException;
}
}
};
/**
* Update the property in the model if two way data binding mode is enabled
*
* @param sName the name of the property to update
* @param oValue the new value to set for the property in the model
* @private
*/
ManagedObject.prototype.updateModelProperty = function(sName, oValue, oOldValue){
if (this.isBound(sName)) {
var oBindingInfo = this.mBindingInfos[sName],
oBinding = oBindingInfo.binding;
// If property change was triggered by the model, don't update the model again
if (oBindingInfo.skipModelUpdate) {
return;
}
// only one property binding should work with two way mode...composite binding does not work with two way binding
if (oBinding && oBinding.getBindingMode() == sap.ui.model.BindingMode.TwoWay) {
try {
// Set flag to avoid originating property to be updated from the model
oBindingInfo.skipPropertyUpdate = true;
oBinding.setExternalValue(oValue);
oBindingInfo.skipPropertyUpdate = false;
// If external value differs from own value after model update,
// update property again
var oExternalValue = oBinding.getExternalValue();
if (oValue != oExternalValue) {
this.updateProperty(sName);
}
// Only fire validation success, if a type is used
if (oBinding.getType()) {
this.fireValidationSuccess({
element: this,
property: sName,
type: oBinding.getType(),
newValue: oValue,
oldValue: oOldValue
}, false, true); // bAllowPreventDefault, bEnableEventBubbling
}
} catch (oException) {
oBindingInfo.skipPropertyUpdate = false;
if (oException instanceof sap.ui.model.ParseException) {
this.fireParseError({
element: this,
property: sName,
type: oBinding.getType(),
newValue: oValue,
oldValue: oOldValue,
exception: oException,
message: oException.message
}, false, true); // bAllowPreventDefault, bEnableEventBubbling
} else if (oException instanceof sap.ui.model.ValidateException) {
this.fireValidationError({
element: this,
property: sName,
type: oBinding.getType(),
newValue: oValue,
oldValue: oOldValue,
exception: oException,
message: oException.message
}, false, true); // bAllowPreventDefault, bEnableEventBubbling
} else {
throw oException;
}
}
}
}
};
/**
* Bind an aggregation to the model.
*
* The bound aggregation will use the given template, clone it for each item
* which exists in the bound list and set the appropriate binding context.
* This is a generic method which can be used to bind any aggregation to the
* model. A managed object may flag aggregations in the metamodel with
* bindable="bindable" to get typed bind<i>Something</i> methods for those aggregations.
*
* @param {string} sName the aggregation to bind
* @param {object} oBindingInfo the binding info
* @param {string} oBindingInfo.path the binding path
* @param {sap.ui.base.ManagedObject} oBindingInfo.template the template to clone for each item in the aggregation
* @param {boolean} [oBindingInfo.templateShareable=true] option to enable that the template will be shared which means that it won't be destroyed or cloned automatically
* @param {function} oBindingInfo.factory the factory function
* @param {number} oBindingInfo.startIndex the first entry of the list to be created
* @param {number} oBindingInfo.length the amount of entries to be created (may exceed the sizelimit of the model)
* @param {sap.ui.model.Sorter|sap.ui.model.Sorter[]} [oBindingInfo.sorter] the initial sort order (optional)
* @param {sap.ui.model.Filter[]} [oBindingInfo.filters] the predefined filters for this aggregation (optional)
* @param {object} [oBindingInfo.parameters] a map of parameters which is passed to the binding
* @param {function} [oBindingInfo.groupHeaderFactory] a factory function to generate custom group visualization (optional)
*
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.bindAggregation = function(sName, oBindingInfo) {
var sPath,
oTemplate,
aSorters,
aFilters,
oMetadata = this.getMetadata(),
oAggregation = oMetadata.getAggregation(sName);
// check whether aggregation exists
if (!oAggregation) {
throw new Error("Aggregation \"" + sName + "\" does not exist in " + this);
}
// Old API compatibility (sName, sPath, oTemplate, oSorter, aFilters)
if (typeof oBindingInfo == "string") {
sPath = arguments[1];
oTemplate = arguments[2];
aSorters = arguments[3];
aFilters = arguments[4];
oBindingInfo = {path: sPath, sorter: aSorters, filters: aFilters};
// allow either to pass the template or the factory function as 3rd parameter
if (oTemplate instanceof ManagedObject) {
oBindingInfo.template = oTemplate;
} else if (typeof oTemplate === "function") {
oBindingInfo.factory = oTemplate;
}
}
// if aggregation is already bound, unbind it first
if (this.isBound(sName)) {
this.unbindAggregation(sName, true);
}
// check whether a template has been provided, which is required for proper processing of the binding
// If aggregation is marked correspondingly in the metadata, factory can be omitted (usually requires an updateXYZ method)
if (!(oBindingInfo.template || oBindingInfo.factory)) {
if ( oAggregation._doesNotRequireFactory ) {
// add a dummy factory as property 'factory' is used to distinguish between property- and list-binding
oBindingInfo.factory = function() {
throw new Error("dummy factory called unexpectedly ");
};
} else {
throw new Error("Missing template or factory function for aggregation " + sName + " of " + this + " !");
}
}
// if we have a template we will create a factory function
if (oBindingInfo.template) {
// set default for templateShareable
if (oBindingInfo.templateShareable === undefined) {
oBindingInfo.templateShareable = true;
}
oBindingInfo.factory = function(sId) {
return oBindingInfo.template.clone(sId);
};
}
// if a model separator is found in the path, extract model name and path
var iSeparatorPos = oBindingInfo.path.indexOf(">");
if (iSeparatorPos > 0) {
oBindingInfo.model = oBindingInfo.path.substr(0, iSeparatorPos);
oBindingInfo.path = oBindingInfo.path.substr(iSeparatorPos + 1);
}
// store binding info to create the binding, as soon as the model is available, or when the model is changed
this.mBindingInfos[sName] = oBindingInfo;
// if the model is already available create the binding
if (this.getModel(oBindingInfo.model)) {
this._bindAggregation(sName, oBindingInfo);
}
return this;
};
ManagedObject.prototype._bindAggregation = function(sName, oBindingInfo) {
var that = this,
oBinding,
fModelChangeHandler = function(oEvent){
var sUpdater = "update" + sName.substr(0,1).toUpperCase() + sName.substr(1);
if (that[sUpdater]) {
var sChangeReason = oEvent && oEvent.getParameter("reason");
if (sChangeReason) {
that[sUpdater](sChangeReason);
} else {
that[sUpdater]();
}
} else {
that.updateAggregation(sName);
}
},
fModelRefreshHandler = function(oEvent){
var sRefresher = "refresh" + sName.substr(0,1).toUpperCase() + sName.substr(1);
if (that[sRefresher]) {
that[sRefresher](oEvent.getParameter("reason"));
} else {
fModelChangeHandler(oEvent);
}
};
var oModel = this.getModel(oBindingInfo.model);
if (this.isTreeBinding(sName)) {
oBinding = oModel.bindTree(oBindingInfo.path, this.getBindingContext(oBindingInfo.model), oBindingInfo.filters, oBindingInfo.parameters);
} else {
oBinding = oModel.bindList(oBindingInfo.path, this.getBindingContext(oBindingInfo.model), oBindingInfo.sorter, oBindingInfo.filters, oBindingInfo.parameters);
}
if (this.bUseExtendedChangeDetection === true) {
oBinding.enableExtendedChangeDetection();
}
oBindingInfo.binding = oBinding;
oBindingInfo.modelChangeHandler = fModelChangeHandler;
oBindingInfo.modelRefreshHandler = fModelRefreshHandler;
oBinding.attachChange(fModelChangeHandler);
oBinding.attachRefresh(fModelRefreshHandler);
oBinding.attachEvents(oBindingInfo.events);
oBinding.initialize();
};
/**
* Unbind the aggregation from the model
*
* @param {string} sName the name of the aggregation
* @param {boolean} bSuppressReset whether the reset to empty aggregation when unbinding should be suppressed
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.unbindAggregation = function(sName, bSuppressReset){
var oBindingInfo = this.mBindingInfos[sName],
oAggregationInfo = this.getMetadata().getAggregation(sName);
if (oBindingInfo) {
if (oBindingInfo.binding) {
oBindingInfo.binding.detachChange(oBindingInfo.modelChangeHandler);
oBindingInfo.binding.detachRefresh(oBindingInfo.modelRefreshHandler);
oBindingInfo.binding.detachEvents(oBindingInfo.events);
}
// remove template if any
if (!oBindingInfo.templateShareable && oBindingInfo.template && oBindingInfo.template.destroy) {
oBindingInfo.template.destroy();
}
delete this.mBindingInfos[sName];
if (!bSuppressReset) {
this[oAggregationInfo._sDestructor]();
}
}
return this;
};
/**
* Generic method which is called, whenever an aggregation binding is changed.
* This method deletes all elements in this aggregation and recreates them
* according to the data model.
* In case a managed object needs special handling for a aggregation binding, it can create
* a typed update-method (e.g. "updateRows") which will be used instead of the
* default behaviour.
*
* @private
*/
ManagedObject.prototype.updateAggregation = function(sName) {
var oBindingInfo = this.mBindingInfos[sName],
oBinding = oBindingInfo.binding,
fnFactory = oBindingInfo.factory,
oAggregationInfo = this.getMetadata().getAggregation(sName), // TODO fix handling of hidden aggregations
oClone,
oNewGroup = null,
sGroupFunction = null,
bGrouped = null,
sGroup = null,
that = this;
this[oAggregationInfo._sDestructor]();
if (this.isTreeBinding(sName)) {
var iNodeIndex = 0,
update = function(aContexts, fnFactory, oBinding, oParent){
jQuery.each(aContexts, function(iIndex, oContext) {
var sId = that.getId() + "-" + iNodeIndex++;
oClone = fnFactory(sId, oContext);
oClone.setBindingContext(oContext, oBindingInfo.model);
oParent[oAggregationInfo._sMutator](oClone); // also sets the Parent
update(oBinding.getNodeContexts(oContext), fnFactory, oBinding, oClone);
});
};
update(oBinding.getRootContexts(), fnFactory, oBinding, this);
} else {
sGroupFunction = oAggregationInfo._sMutator + "Group";
bGrouped = oBinding.isGrouped() && this[sGroupFunction];
jQuery.each(oBinding.getContexts(oBindingInfo.startIndex, oBindingInfo.length), function(iIndex, oContext) {
if (bGrouped && oBinding.aSorters.length > 0) {
oNewGroup = oBinding.aSorters[0].fnGroup(oContext);
if (typeof oNewGroup == "string") {
oNewGroup = {
key: oNewGroup
};
}
if (oNewGroup.key !== sGroup) {
var oGroupHeader;
//If factory is defined use it
if (oBindingInfo.groupHeaderFactory) {
oGroupHeader = oBindingInfo.groupHeaderFactory(oNewGroup);
}
that[sGroupFunction](oNewGroup, oGroupHeader);
sGroup = oNewGroup.key;
}
}
var sId = that.getId() + "-" + iIndex;
oClone = fnFactory(sId, oContext);
oClone.setBindingContext(oContext, oBindingInfo.model);
that[oAggregationInfo._sMutator](oClone);
});
}
};
/**
* Generic method which can be called, when an aggregation needs to be refreshed.
* This method does not make any change on the aggregtaion, but just calls the
* getContexts method to trigger fetching of new data.
*
* @private
*/
ManagedObject.prototype.refreshAggregation = function(sName) {
var oBindingInfo = this.mBindingInfos[sName],
oBinding = oBindingInfo.binding;
oBinding.getContexts(oBindingInfo.startIndex, oBindingInfo.length);
};
/**
* Generic method which is called, whenever messages for this object exists.
*
* @param {string} sName The property name
* @param {array} aMessages The messages
* @protected
* @since 1.28
*/
ManagedObject.prototype.updateMessages = function(sName, aMessages) {
jQuery.sap.log.warning("Message for " + this + ", Property " + sName);
};
/**
* This method is used internally and should only be overridden by a tree managed object which utilizes the tree binding.
* In this case and if the aggregation is a tree node the overridden method should then return true.
* If true is returned the tree binding will be used instead of the list binding.
*
* @param {string} sName the aggregation to bind (e.g. nodes for a tree managed object)
* @return {boolean} whether tree binding should be used or list binding. Default is false. Override method to change this behavior.
*
* @protected
*/
ManagedObject.prototype.isTreeBinding = function(sName) {
return false;
};
/**
* Create or update local bindings.
*
* Called when model or binding contexts have changed. Creates bindings when the model was not available
* at the time bindProperty or bindAggregation was called. Recreates the bindings when they exist already
* and when the model has changed.
*
* @param {boolean} bUpdateAll forces an update of all bindings, sModelName will be ignored
* @param {string|undefined} sModelName name of a model whose bindings should be updated
*
* @private
*/
ManagedObject.prototype.updateBindings = function(bUpdateAll, sModelName) {
var that = this;
/*
* Checks whether the binding for the given oBindingInfo became invalid because
* of the current model change (as identified by bUpdateAll and sModelName).
*
* Precondition: oBindingInfo contains a 'binding' object
*
* @param oBindingInfo
* @returns {boolean}
*/
function becameInvalid(oBindingInfo) {
var aParts = oBindingInfo.parts,
i;
if (aParts && aParts.length > 1) {
// composite binding: invalid when for any part the model has the same name (or updateall) and when the model instance for that part differs
for (i = 0; i < aParts.length; i++) {
if ( (bUpdateAll || aParts[i].model == sModelName) && !oBindingInfo.binding.aBindings[i].updateRequired(that.getModel(aParts[i].model)) ) {
return true;
}
}
} else if (oBindingInfo.factory) {
// list binding: invalid when the model has the same name (or updateall) and when the model instance differs
return (bUpdateAll || oBindingInfo.model == sModelName) && !oBindingInfo.binding.updateRequired(that.getModel(oBindingInfo.model));
} else {
// simple property binding: invalid when the model has the same name (or updateall) and when the model instance differs
return (bUpdateAll || aParts[0].model == sModelName) && !oBindingInfo.binding.updateRequired(that.getModel(aParts[0].model));
}
return false;
}
/*
* Checks whether a binding can be created for the given oBindingInfo
* @param oBindingInfo
* @returns {boolean}
*/
function canCreate(oBindingInfo) {
var aParts = oBindingInfo.parts,
i;
if (aParts) {
for (i = 0; i < aParts.length; i++) {
if ( !that.getModel(aParts[i].model) ) {
return false;
}
}
return true;
} else if (oBindingInfo.factory) { // List binding check
return !!that.getModel(oBindingInfo.model);
}
// there should be no other cases
return false;
}
// create property and aggregation bindings if they don't exist yet
jQuery.each(this.mBindingInfos, function(sName, oBindingInfo) {
// if there is a binding and if it became invalid through the current model change, then remove it
if ( oBindingInfo.binding && becameInvalid(oBindingInfo) ) {
oBindingInfo.binding.detachChange(oBindingInfo.modelChangeHandler);
if (oBindingInfo.modelRefreshHandler) { // only list bindings currently have a refresh handler attached
oBindingInfo.binding.detachRefresh(oBindingInfo.modelRefreshHandler);
}
oBindingInfo.binding.detachEvents(oBindingInfo.events);
delete oBindingInfo.binding;
}
// if there is no binding and if all required information is available, create a binding object
if ( !oBindingInfo.binding && canCreate(oBindingInfo) ) {
if (oBindingInfo.factory) {
that._bindAggregation(sName, oBindingInfo);
} else {
that._bindProperty(sName, oBindingInfo);
}
}
});
};
/**
* Find out whether a property or aggregation is bound
*
* @param {string} sName the name of the property or aggregation
* @return {boolean} whether a binding exists for the given name
* @public
*/
ManagedObject.prototype.isBound = function(sName){
return (sName in this.mBindingInfos);
};
/**
* Get the object binding object for a specific model
*
* @param {string} sModelName the name of the model
* @return {sap.ui.model.Binding} the element binding for the given model name
* @public
*/
ManagedObject.prototype.getObjectBinding = function(sModelName){
return this.mBoundObjects[sModelName] && this.mBoundObjects[sModelName].binding;
};
/**
* Returns the parent managed object as new eventing parent to enable control event bubbling
* or <code>null</code> if this object hasn't been added to a parent yet.
*
* @return {sap.ui.base.EventProvider} the parent event provider
* @protected
*/
ManagedObject.prototype.getEventingParent = function() {
return this.oParent;
};
/**
* Get the binding object for a specific aggregation/property
*
* @param {string} sName the name of the property or aggregation
* @return {sap.ui.model.Binding} the binding for the given name
* @public
*/
ManagedObject.prototype.getBinding = function(sName){
return this.mBindingInfos[sName] && this.mBindingInfos[sName].binding;
};
/**
* Get the binding path for a specific aggregation/property
*
* @param {string} sName the name of the property or aggregation
* @return {string} the binding path for the given name
* @protected
*/
ManagedObject.prototype.getBindingPath = function(sName){
var oInfo = this.mBindingInfos[sName];
return oInfo && (oInfo.path || (oInfo.parts && oInfo.parts[0] && oInfo.parts[0].path));
};
/**
* Set the binding context for this ManagedObject for the model with the given name.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* Note: A ManagedObject inherits binding contexts from the Core only when it is a descendant of an UIArea.
*
* @param {Object} oContext the new binding context for this object
* @param {string} [sModelName] the name of the model to set the context for or <code>undefined</code>
*
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
ManagedObject.prototype.setBindingContext = function(oContext, sModelName){
jQuery.sap.assert(sModelName === undefined || (typeof sModelName === "string" && !/^(undefined|null)?$/.test(sModelName)), "sModelName must be a string or omitted");
var oOldContext = this.oBindingContexts[sModelName];
if (oOldContext !== oContext) {
this.oBindingContexts[sModelName] = oContext;
this.updateBindingContext(false, true, sModelName);
this.propagateProperties(sModelName);
}
return this;
};
/**
* @private
*/
ManagedObject.prototype.setElementBindingContext = function(oContext, sModelName){
jQuery.sap.assert(sModelName === undefined || (typeof sModelName === "string" && !/^(undefined|null)?$/.test(sModelName)), "sModelName must be a string or omitted");
var oOldContext = this.mElementBindingContexts[sModelName];
if (oOldContext !== oContext) {
this.mElementBindingContexts[sModelName] = oContext;
this.updateBindingContext(true, true, sModelName);
this.propagateProperties(sModelName);
}
return this;
};
/**
* Update the binding context in this object and all aggregated children
* @private
*/
ManagedObject.prototype.updateBindingContext = function(bSkipLocal, bSkipChildren, sFixedModelName, bUpdateAll){
var oModel,
oModelNames = {},
sModelName,
oContext,
oBoundObject,
that = this;
// find models that need an context update
if (bUpdateAll) {
for (sModelName in this.oModels) {
if ( this.oModels.hasOwnProperty(sModelName) ) {
oModelNames[sModelName] = sModelName;
}
}
for (sModelName in this.oPropagatedProperties.oModels) {
if ( this.oPropagatedProperties.oModels.hasOwnProperty(sModelName) ) {
oModelNames[sModelName] = sModelName;
}
}
} else {
oModelNames[sFixedModelName] = sFixedModelName;
}
/*eslint-disable no-loop-func */
for (sModelName in oModelNames ) {
if ( oModelNames.hasOwnProperty(sModelName) ) {
sModelName = sModelName === "undefined" ? undefined : sModelName;
oModel = this.getModel(sModelName);
oBoundObject = this.mBoundObjects[sModelName];
if (oModel && oBoundObject && oBoundObject.sBindingPath && !bSkipLocal) {
if (!oBoundObject.binding) {
this._bindObject(sModelName, oBoundObject);
} else {
oContext = this._getBindingContext(sModelName);
if (oContext !== oBoundObject.binding.getContext()) {
oBoundObject.binding.setContext(oContext);
}
}
continue;
}
// update context in existing bindings
jQuery.each(this.mBindingInfos, function(sName, oBindingInfo) {
var oBinding = oBindingInfo.binding;
var aParts = oBindingInfo.parts,
i;
if (!oBinding) {
return;
}
if (aParts && aParts.length > 1) {
// composite binding: update required when a part use the model with the same name
for (i = 0; i < aParts.length; i++) {
if ( aParts[i].model == sModelName ) {
oBinding.aBindings[i].setContext(that.getBindingContext(aParts[i].model));
}
}
} else if (oBindingInfo.factory) {
// list binding: update required when the model has the same name (or updateall)
if ( oBindingInfo.model == sModelName) {
oBinding.setContext(that.getBindingContext(oBindingInfo.model));
}
} else {
// simple property binding: update required when the model has the same name
if ( aParts[0].model == sModelName) {
oBinding.setContext(that.getBindingContext(aParts[0].model));
}
}
});
if (!bSkipChildren) {
var oContext = this.getBindingContext(sModelName);
// also update context in all child elements
for (var sName in this.mAggregations) {
var oAggregation = this.mAggregations[sName];
if (oAggregation instanceof ManagedObject) {
oAggregation.oPropagatedProperties.oBindingContexts[sModelName] = oContext;
oAggregation.updateBindingContext(false,false,sModelName);
} else if (oAggregation instanceof Array) {
for (var i = 0; i < oAggregation.length; i++) {
oAggregation[i].oPropagatedProperties.oBindingContexts[sModelName] = oContext;
oAggregation[i].updateBindingContext(false,false,sModelName);
}
}
}
}
}
}
/*eslint-enable no-loop-func */
};
/**
* Get the binding context of this object for the given model name.
*
* If the object does not have a binding context set on itself and has no own Model set,
* it will use the first binding context defined in its parent hierarchy.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* Note: A ManagedObject inherits binding contexts from the Core only when it is a descendant of an UIArea.
*
* @param {string} [sModelName] the name of the model or <code>undefined</code>
* @return {Object} the binding context of this object
* @public
*/
ManagedObject.prototype.getBindingContext = function(sModelName){
if (this.mElementBindingContexts[sModelName]) {
return this.mElementBindingContexts[sModelName];
}
return this._getBindingContext(sModelName);
};
/**
* Get the binding context of this object for the given model name. The elementBindingContext will not be considered
* @private
*/
ManagedObject.prototype._getBindingContext = function(sModelName){
var oModel = this.getModel(sModelName);
if (this.oBindingContexts[sModelName]) {
return this.oBindingContexts[sModelName];
} else if (oModel && this.oParent && this.oParent.getModel(sModelName) && oModel != this.oParent.getModel(sModelName)) {
return undefined;
} else {
return this.oPropagatedProperties.oBindingContexts[sModelName];
}
};
/**
* Sets or unsets a model for the given model name for this ManagedObject.
*
* The <code>sName</code> must either be <code>undefined</code> (or omitted) or a non-empty string.
* When the name is omitted, the default model is set/unset.
*
* When <code>oModel</code> is <code>null</code> or <code>undefined</code>, a previously set model
* with that name is removed from this ManagedObject. If an ancestor (parent, UIArea or Core) has a model
* with that name, this ManagedObject will immediately inherit that model from its ancestor.
*
* All local bindings that depend on the given model name, are updated (created if the model references
* became complete now; updated, if any model reference has changed; removed if the model references
* became incomplete now).
*
* Any change (new model, removed model, inherited model) is also applied to all aggregated descendants
* as long as a descendant doesn't have its own model set for the given name.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* Note: By design, it is not possible to hide an inherited model by setting a <code>null</code> or
* <code>undefined</code> model. Applications can set an empty model to achieve the same.
*
* Note: A ManagedObject inherits models from the Core only when it is a descendant of an UIArea.
*
* @param {sap.ui.model.Model} oModel the model to be set or <code>null</code> or <code>undefined</code>
* @param {string} [sName] the name of the model or <code>undefined</code>
* @return {sap.ui.base.ManagedObject} <code>this</code> to allow method chaining
* @public
*/
ManagedObject.prototype.setModel = function(oModel, sName) {
jQuery.sap.assert(oModel == null || oModel instanceof Model, "oModel must be an instance of sap.ui.model.Model, null or undefined");
jQuery.sap.assert(sName === undefined || (typeof sName === "string" && !/^(undefined|null)?$/.test(sName)), "sName must be a string or omitted");
if (!oModel && this.oModels[sName]) {
delete this.oModels[sName];
// propagate Models to children
// model changes are propagated until (including) the first descendant that has its own model with the same name
this.propagateProperties(sName);
// if the model instance for a name changes, all bindings for that model name have to be updated
this.updateBindings(false, sName);
} else if ( oModel && oModel !== this.oModels[sName] ) {
//TODO: handle null!
this.oModels[sName] = oModel;
// propagate Models to children
// model changes are propagated until (including) the first descendant that has its own model with the same name
this.propagateProperties(sName);
// update binding context, for primary model only
this.updateBindingContext(false, true, sName);
// if the model instance for a name changes, all bindings for that model name have to be updated
this.updateBindings(false, sName);
} // else nothing to do
return this;
};
/**
* Propagate Properties (models and bindingContext) to aggregated objects.
* @param {string|undefined|true} sName when <code>true</code>, all bindings are updated.
* Otherwise only those for the given model name (undefined == name of default model)
*
* @private
*/
ManagedObject.prototype.propagateProperties = function(vName) {
var oProperties = this._getPropertiesToPropagate(),
bUpdateAll = vName === true, // update all bindings when no model name parameter has been specified
sName = bUpdateAll ? undefined : vName,
sAggregationName, oAggregation, i;
for (sAggregationName in this.mAggregations) {
if (this.mSkipPropagation[sAggregationName]) {
continue;
}
oAggregation = this.mAggregations[sAggregationName];
if (oAggregation instanceof ManagedObject) {
this._propagateProperties(vName, oAggregation, oProperties, bUpdateAll, sName);
} else if (oAggregation instanceof Array) {
for (i = 0; i < oAggregation.length; i++) {
if (oAggregation[i] instanceof ManagedObject) {
this._propagateProperties(vName, oAggregation[i], oProperties, bUpdateAll, sName);
}
}
}
}
};
ManagedObject.prototype._propagateProperties = function(vName, oObject, oProperties, bUpdateAll, sName) {
if (!oProperties) {
oProperties = this._getPropertiesToPropagate();
bUpdateAll = vName === true;
sName = bUpdateAll ? undefined : vName;
}
oObject.oPropagatedProperties = oProperties;
oObject.updateBindings(bUpdateAll,sName);
oObject.updateBindingContext(false, true, sName, bUpdateAll);
oObject.propagateProperties(vName);
};
/**
* Get properties for propagation
* @return {object} oProperties
* @private
*/
ManagedObject.prototype._getPropertiesToPropagate = function() {
var bNoOwnModels = jQuery.isEmptyObject(this.oModels),
bNoOwnContexts = jQuery.isEmptyObject(this.oBindingContexts),
bNoOwnElementContexts = jQuery.isEmptyObject(this.mElementBindingContexts);
function merge(empty,o1,o2,o3) {
return empty ? o1 : jQuery.extend({}, o1, o2, o3);
}
if (bNoOwnContexts && bNoOwnModels && bNoOwnElementContexts) {
//propagate the existing container
return this.oPropagatedProperties;
} else {
//merge propagated and own properties
return {
oModels : merge(bNoOwnModels, this.oPropagatedProperties.oModels, this.oModels),
oBindingContexts : merge((bNoOwnContexts && bNoOwnElementContexts), this.oPropagatedProperties.oBindingContexts, this.oBindingContexts, this.mElementBindingContexts)
};
}
};
/**
* Get the model to be used for data bindings with the given model name.
* If the object does not have a model set on itself, it will use the first
* model defined in its parent hierarchy.
*
* The name can be omitted to reference the default model or it must be a non-empty string.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* @param {string|undefined} [sName] name of the model to be retrieved
* @return {sap.ui.model.Model} oModel
* @public
*/
ManagedObject.prototype.getModel = function(sName) {
jQuery.sap.assert(sName === undefined || (typeof sName === "string" && !/^(undefined|null)?$/.test(sName)), "sName must be a string or omitted");
return this.oModels[sName] || this.oPropagatedProperties.oModels[sName];
};
/**
* Check if any model is set to the ManagedObject or to one of its parents (including UIArea and Core).
*
* Note: A ManagedObject inherits models from the Core only when it is a descendant of an UIArea.
*
* @return {boolean} whether a model reference exists or not
* @public
*/
ManagedObject.prototype.hasModel = function() {
return !(jQuery.isEmptyObject(this.oModels) && jQuery.isEmptyObject(this.oPropagatedProperties.oModels));
};
/**
* Clones a tree of objects starting with the object on which clone is called first (root object).
*
* The ids within the newly created clone tree are derived from the original ids by appending
* the given <code>sIdSuffix</code> (if no suffix is given, one will be created; it will be
* unique across multiple clone calls).
*
* The <code>oOptions</code> configuration object can have the following properties:
* <ul>
* <li>The boolean value <code>cloneChildren</code> specifies wether associations/aggregations will be cloned</li>
* <li>The boolean value <code>cloneBindings</code> specifies if bindings will be cloned</li>
* </ul>
*
* For each cloned object the following settings are cloned based on the metadata of the object and the defined options:
* <ul>
* <li>all properties that are not bound. If cloneBinding is false even these properties will be cloned;
* the values are used by reference, they are not cloned</li>
* <li>all aggregated objects that are not bound. If cloneBinding is false even the ones that are bound will be cloned;
* they are all cloned recursively using the same <code>sIdSuffix</code></li>
* <li>all associated controls; when an association points to an object inside the cloned object tree,
* then the cloned association will be modified to that it points to the clone of the target object.
* When the association points to a managed object outside of the cloned object tree, then its
* target won't be changed.</li>
* <li>all models set via setModel(); used by reference </li>
* <li>all property and aggregation bindings (if cloneBindings is true); the pure binding infos (path, model name) are
* cloned, but all other information like template control or factory function,
* data type or formatter function are copied by reference. The bindings themselves
* are created anew as they are specific for the combination (object, property, model).
* As a result, any later changes to a binding of the original object are not reflected
* in the clone, but changes to e.g the type or template etc. are.</li>
* </ul>
*
* Each clone is created by first collecting the above mentioned settings and then creating
* a new instance with the normal constructor function. As a result, any side effects of
* mutator methods (setProperty etc.) or init hooks are repeated during clone creation.
* There is no need to override <code>clone()</code> just to reproduce these internal settings!
*
* Custom controls however can override <code>clone()</code> to implement additional clone steps.
* They usually will first call <code>clone()</code> on the super class and then modify the
* returned clone accordingly.
*
* Applications <b>must never provide</b> the second parameter <code>aLocaleIds</code>.
* It is determined automatically for the root object (and its non-existance also serves as
* an indicator for the root object). Specifying it will break the implementation of <code>clone()</code>.
*
* @param {string} [sIdSuffix] a suffix to be appended to the cloned object id
* @param {string[]} [aLocalIds] an array of local IDs within the cloned hierarchy (internally used)
* @param {Object} [oOptions] configuration object
* @return {sap.ui.base.ManagedObject} reference to the newly created clone
* @protected
*/
ManagedObject.prototype.clone = function(sIdSuffix, aLocalIds, oOptions) {
var bCloneChildren = true,
bCloneBindings = true;
if (oOptions) {
bCloneChildren = !!oOptions.cloneChildren;
bCloneBindings = !!oOptions.cloneBindings;
}
// if no id suffix has been provided use a generated UID
if (!sIdSuffix) {
sIdSuffix = ManagedObjectMetadata.uid("clone") || jQuery.sap.uid();
}
// if no local ID array has been passed, collect IDs of all aggregated objects to
// be able to properly adapt associations, which are within the cloned object hierarchy
if (!aLocalIds && bCloneChildren) {
aLocalIds = jQuery.map(this.findAggregatedObjects(true), function(oObject) {
return oObject.getId();
});
}
var oMetadata = this.getMetadata(),
oClass = oMetadata._oClass,
sId = this.getId() + "-" + sIdSuffix,
mSettings = {},
mProps = this.mProperties,
sKey,
sName,
oClone,
escape = ManagedObject.bindingParser.escape;
// Clone properties (only those with non-default value)
for (sKey in mProps) {
//do not clone properties if property is bound and bindings are cloned; Property is set on update
if ( mProps.hasOwnProperty(sKey) && !(this.isBound(sKey) && bCloneBindings)) {
// Note: to avoid double resolution of binding expressions, we have to escape string values once again
if (typeof mProps[sKey] === "string") {
mSettings[sKey] = escape(mProps[sKey]);
} else {
mSettings[sKey] = mProps[sKey];
}
}
}
// Clone models
mSettings["models"] = this.oModels;
// Clone BindingContext
mSettings["bindingContexts"] = this.oBindingContexts;
if (bCloneChildren) {
// Clone aggregations
for (sName in this.mAggregations) {
var oAggregation = this.mAggregations[sName];
//do not clone aggregation if aggregation is bound and bindings are cloned; aggregation is filled on update
if (oMetadata.hasAggregation(sName) && !(this.isBound(sName) && bCloneBindings)) {
if (oAggregation instanceof ManagedObject) {
mSettings[sName] = oAggregation.clone(sIdSuffix, aLocalIds);
} else if (jQuery.isArray(oAggregation)) {
mSettings[sName] = [];
for (var i = 0; i < oAggregation.length; i++) {
mSettings[sName].push(oAggregation[i].clone(sIdSuffix, aLocalIds));
}
} else {
// must be an alt type
mSettings[sName] = oAggregation;
}
}
}
// Clone associations
for (sName in this.mAssociations) {
var oAssociation = this.mAssociations[sName];
// Check every associated ID against the ID array, to make sure associations within
// the template are properly converted to associations within the clone
if (jQuery.isArray(oAssociation)) {
oAssociation = oAssociation.slice(0);
for (var i = 0; i < oAssociation.length; i++) {
if (jQuery.inArray(oAssociation[i], aLocalIds) >= 0) {
oAssociation[i] += "-" + sIdSuffix;
}
}
} else if (jQuery.inArray(oAssociation, aLocalIds) >= 0) {
oAssociation += "-" + sIdSuffix;
}
mSettings[sName] = oAssociation;
}
}
// Create clone instance
oClone = new oClass(sId, mSettings);
/* Clone element bindings: Clone the objects not the parameters
* Context will only be updated when adding the control to the control tree;
* Maybe we have to call updateBindingcontext() here?
*/
for (sName in this.mBoundObjects) {
oClone.mBoundObjects[sName] = jQuery.extend({}, this.mBoundObjects[sName]);
}
// Clone events
for (sName in this.mEventRegistry) {
oClone.mEventRegistry[sName] = this.mEventRegistry[sName].slice();
}
// Clone bindings
if (bCloneBindings) {
for (sName in this.mBindingInfos) {
var oBindingInfo = this.mBindingInfos[sName];
var oCloneBindingInfo = jQuery.extend({}, oBindingInfo);
// clone the template if it is not shared
if (!oBindingInfo.templateShareable && oBindingInfo.template && oBindingInfo.template.clone) {
oCloneBindingInfo.template = oBindingInfo.template.clone(sIdSuffix, aLocalIds);
delete oCloneBindingInfo.factory;
}
delete oCloneBindingInfo.binding; // remove the runtime binding info (otherwise the property will not be connected again!)
if (oBindingInfo.factory || oBindingInfo.template) {
oClone.bindAggregation(sName, oCloneBindingInfo);
} else {
oClone.bindProperty(sName, oCloneBindingInfo);
}
}
}
return oClone;
};
/**
* Update all localization dependant objects that this managedObject can reach,
* except for its children (which will be updated from the Core).
*
* To make the update work as smooth as possible, it happens in two phases:
* <ol>
* <li>In phase 1 all known models are updated.
* <li>In phase 2 all bindings are updated.
* </ol>
* This separation is necessary as the models for the bindings might be updated
* in some ManagedObject or in the Core and the order in which the objects are visited
* is not defined (Core.mElements order)
*
* @private
*/
ManagedObject._handleLocalizationChange = function(iPhase) {
var i;
if ( iPhase === 1 ) {
/*
* phase 1: update the models
*/
jQuery.each(this.oModels, function(sName, oModel) {
if ( oModel && oModel._handleLocalizationChange ) {
oModel._handleLocalizationChange();
}
});
} else if ( iPhase === 2 ) {
/*
* phase 2: update bindings and types
*/
jQuery.each(this.mBindingInfos, function(sName, oBindingInfo) {
var aParts = oBindingInfo.parts;
if (aParts) {
// property or composite binding: visit all parts
for (i = 0; i < aParts.length; i++) {
if ( oBindingInfo.type && oBindingInfo.type._handleLocalizationChange ) {
oBindingInfo.type._handleLocalizationChange();
}
}
if ( oBindingInfo.modelChangeHandler ) {
oBindingInfo.modelChangeHandler();
}
} // else: don't update list bindings
// Note: the template for a list binding will be visited by the core!
});
}
};
/**
* Maps the given aggregation with name <code>sOldAggrName</code>
* on aggregation <code>sNewAggrName</code> (When calling an accessor function
* of the old aggregation the call is forwarded to the corresponding accessor
* function of the new aggregation).
*
* This function should help to perform a smooth transition for users of a managed object
* when an aggregation must be renamed.
*
* Both aggregations must have a mutiple cardinality (0..n) and must have the same
* aggregated type!
*
* @param {object} oPrototype prototype of the ManagedObject class for which a mapping should be defined
* @param {string} sOldAggrName Name of the old deprecated aggregation
* @param {string} sNewAggrName Name of the new aggregation
* @deprecated
*/
ManagedObject._mapAggregation = function(oPrototype, sOldAggrName, sNewAggrName){
var mKeys = oPrototype.getMetadata().getAllSettings();
var oOldAggrInfo = mKeys[sOldAggrName];
var oNewAggrInfo = mKeys[sNewAggrName];
//Check whether aggregations exist and are multiple.
if (!oOldAggrInfo || !oNewAggrInfo || oOldAggrInfo._iKind != 2 || oNewAggrInfo._iKind != 2) {
return;
}
var mFunc = {"insert" : true, "add" : true, "remove" : true, "removeAll" : false, "indexOf" : true, "destroy" : false, "get" : false};
function method(sPrefix, sName) {
return sPrefix + sName.substring(0,1).toUpperCase() + sName.substring(1);
}
function fAggrDelegator(sFuncName){
return function() {
return this[sFuncName].apply(this, arguments);
};
}
for (var sPrefix in mFunc) {
var sOldFuncName = method(sPrefix, mFunc[sPrefix] ? oOldAggrInfo.singularName : oOldAggrInfo.name);
var sNewFuncName = method(sPrefix, mFunc[sPrefix] ? oNewAggrInfo.singularName : oNewAggrInfo.name);
oPrototype[sOldFuncName] = fAggrDelegator(sNewFuncName);
}
};
/**
* Searches and returns an array of child elements and controls which are
* referenced within an aggregation or aggregations of child elements/controls.
* This can be either done recursive or not.
* <br>
* <b>Take care: this operation might be expensive.</b>
* @param {boolean}
* bRecursive true, if all nested children should be returned.
* @return {sap.ui.base.ManagedObject[]} array of child elements and controls
* @public
*/
ManagedObject.prototype.findAggregatedObjects = function(bRecursive) {
var aAggregatedObjects = [];
function fFindObjects(oObject) {
for (var n in oObject.mAggregations) {
var a = oObject.mAggregations[n];
if (jQuery.isArray(a)) {
for (var i = 0; i < a.length; i++) {
aAggregatedObjects.push(a[i]);
if (bRecursive) {
fFindObjects(a[i]);
}
}
} else if (a instanceof ManagedObject) {
aAggregatedObjects.push(a);
if (bRecursive) {
fFindObjects(a);
}
}
}
}
fFindObjects(this);
return aAggregatedObjects;
};
return ManagedObject;
}, /* bExport= */ true);
},
"sap/ui/base/ManagedObjectMetadata.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.ManagedObjectMetadata
sap.ui.define(['jquery.sap.global', './DataType', './Metadata'],
function(jQuery, DataType, Metadata) {
"use strict";
/**
* Creates a new metadata object that describes a subclass of ManagedObject.
*
* Note: throughout this class documentation, the described subclass of ManagedObject
* is referenced as <i>the described class</i>.
*
* @param {string} sClassName fully qualified name of the described class
* @param {object} oClassInfo static info to construct the metadata from
*
* @class
* @classdesc
*
* <strong>Note about Info Objects</strong>
*
* Several methods in this class return info objects that describe a property,
* aggregation, association or event of the class described by this metadata object.
* The type, structure and behavior of these info objects is not yet documented and
* not part of the stable, public API.
*
* Code using such methods and the returned info objects therefore needs to be aware
* of the following restrictions:
*
* <ul>
* <li>the set of properties exposed by each info object, their type and value
* might change as well as the class of the info object itself.
*
* Properties that represent settings provided during class definition
* (in the oClassInfo parameter of the 'extend' call, e.g. 'type', 'multiple'
* of an aggregation) are more likely to stay the same than additional, derived
* properties like '_iKind'.</li>
*
* <li>info objects must not be modified / enriched although they technically could.</li>
*
* <li>the period of validity of info objects is not defined. They should be
* referenced only for a short time and not be kept as members of long living
* objects or closures.</li>
*
* </ul>
*
* @author Frank Weigel
* @version 1.28.5
* @since 0.8.6
* @alias sap.ui.base.ManagedObjectMetadata
*/
var ManagedObjectMetadata = function(sClassName, oClassInfo) {
// call super constructor
Metadata.apply(this, arguments);
};
// chain the prototypes
ManagedObjectMetadata.prototype = jQuery.sap.newObject(Metadata.prototype);
var hasOwnProperty = Object.prototype.hasOwnProperty;
function capitalize(sName) {
return sName.charAt(0).toUpperCase() + sName.slice(1);
}
var rPlural = /(children|ies|ves|oes|ses|ches|shes|xes|s)$/i;
var mSingular = {'children' : -3, 'ies' : 'y', 'ves' : 'f', 'oes' : -2, 'ses' : -2, 'ches' : -2, 'shes' : -2, 'xes' : -2, 's' : -1 };
function guessSingularName(sName) {
return sName.replace(rPlural, function($,sPlural) {
var vRepl = mSingular[sPlural.toLowerCase()];
return typeof vRepl === "string" ? vRepl : sPlural.slice(0,vRepl);
});
}
function deprecation(fn, name) {
return function() {
jQuery.sap.log.warning("Usage of deprecated feature: " + name);
return fn.apply(this, arguments);
};
}
function remainder(obj, info) {
var result = null;
for (var n in info) {
if ( hasOwnProperty.call(info, n) && typeof obj[n] === 'undefined' ) {
result = result || {};
result[n] = info[n];
}
}
return result;
}
var Kind = {
SPECIAL_SETTING : -1, PROPERTY : 0, SINGLE_AGGREGATION : 1, MULTIPLE_AGGREGATION : 2, SINGLE_ASSOCIATION : 3, MULTIPLE_ASSOCIATION : 4, EVENT : 5
};
/**
* Guess a singular name for a given plural name.
*
* This method is not guaranteed to return a valid result. If the result is not satisfying,
* the singular name for an aggregation/association should be specified in the class metadata.
*
* @private
* @function
*/
ManagedObjectMetadata._guessSingularName = guessSingularName;
// ---- SpecialSetting --------------------------------------------------------------------
/**
/* SpecialSetting info object
* @private
* @since 1.27.1
*/
function SpecialSetting(oClass, name, info) {
info = typeof info !== 'object' ? { type: info } : info;
this.name = name;
this.type = info.type || 'any';
this._oParent = oClass;
this._sUID = "special:" + name;
this._iKind = Kind.SPECIAL_SETTING;
}
// ---- Property --------------------------------------------------------------------------
/**
/* Property info object
* @private
* @since 1.27.1
*/
function Property(oClass, name, info) {
info = typeof info !== 'object' ? { type: info } : info;
this.name = name;
this.type = info.type || 'string';
this.group = info.group || 'Misc';
this.defaultValue = info.defaultValue !== null ? info.defaultValue : null;
this.bindable = !!info.bindable;
this.deprecated = !!info.deprecated || false;
this.visibility = 'public';
this.appData = remainder(this, info);
this._oParent = oClass;
this._sUID = name;
this._iKind = Kind.PROPERTY;
var N = capitalize(name);
this._sMutator = 'set' + N;
this._sGetter = 'get' + N;
if ( this.bindable ) {
this._sBind = 'bind' + N;
this._sUnbind = 'unbind' + N;
} else {
this._sBind =
this._sUnbind = undefined;
}
this._oType = null;
}
/**
* @private
*/
Property.prototype.generate = function(add) {
var that = this,
n = that.name;
add(that._sGetter, function() { return this.getProperty(n); });
add(that._sMutator, function(v) { this.setProperty(n,v); return this; }, that);
if ( that.bindable ) {
add(that._sBind, function(p,fn,m) { this.bindProperty(n,p,fn,m); return this; }, that);
add(that._sUnbind, function(p) { this.unbindProperty(n,p); return this; });
}
};
Property.prototype.getType = function() {
return this._oType || (this._oType = DataType.getType(this.type));
};
Property.prototype.get = function(that) {
return that[this._sGetter]();
};
Property.prototype.set = function(that, oValue) {
return that[this._sMutator](oValue);
};
// ---- Aggregation -----------------------------------------------------------------------
/**
* Aggregation info object
* @private
* @since 1.27.1
*/
function Aggregation(oClass, name, info) {
info = typeof info !== 'object' ? { type: info } : info;
this.name = name;
this.type = info.type || 'sap.ui.core.Control';
this.altTypes = info.altTypes || undefined;
this.multiple = typeof info.multiple === 'boolean' ? info.multiple : true;
this.singularName = this.multiple ? info.singularName || guessSingularName(name) : undefined;
this.bindable = !!info.bindable;
this.deprecated = info.deprecated || false;
this.visibility = info.visibility || 'public';
this._doesNotRequireFactory = !!info._doesNotRequireFactory; // TODO clarify if public
this.appData = remainder(this, info);
this._oParent = oClass;
this._sUID = 'aggregation:' + name;
this._iKind = this.multiple ? Kind.MULTIPLE_AGGREGATION : Kind.SINGLE_AGGREGATION;
var N = capitalize(name);
this._sGetter = 'get' + N;
if ( this.multiple ) {
var N1 = capitalize(this.singularName);
this._sMutator = 'add' + N1;
this._sInsertMutator = 'insert' + N1;
this._sRemoveMutator = 'remove' + N1;
this._sRemoveAllMutator = 'removeAll' + N;
this._sIndexGetter = 'indexOf' + N1;
} else {
this._sMutator = 'set' + N;
this._sInsertMutator =
this._sRemoveMutator =
this._sRemoveAllMutator =
this._sIndexGetter = undefined;
}
this._sDestructor = 'destroy' + N;
if ( this.bindable ) {
this._sBind = 'bind' + N;
this._sUnbind = 'unbind' + N;
} else {
this._sBind =
this._sUnbind = undefined;
}
}
/**
* @private
*/
Aggregation.prototype.generate = function(add) {
var that = this,
n = that.name;
if ( !that.multiple ) {
add(that._sGetter, function() { return this.getAggregation(n); });
add(that._sMutator, function(v) { this.setAggregation(n,v); return this; }, that);
} else {
add(that._sGetter, function() { return this.getAggregation(n,[]); });
add(that._sMutator, function(a) { this.addAggregation(n,a); return this; }, that);
add(that._sInsertMutator, function(i,a) { this.insertAggregation(n,i,a); return this; }, that);
add(that._sRemoveMutator, function(a) { return this.removeAggregation(n,a); });
add(that._sRemoveAllMutator, function() { return this.removeAllAggregation(n); });
add(that._sIndexGetter, function(a) { return this.indexOfAggregation(n,a); });
}
add(that._sDestructor, function() { this.destroyAggregation(n); return this; });
if ( that.bindable ) {
add(that._sBind, function(p,t,s,f) { this.bindAggregation(n,p,t,s,f); return this; }, that);
add(that._sUnbind, function(p) { this.unbindAggregation(n,p); return this; });
}
};
Aggregation.prototype.getType = function() {
return this._oType || (this._oType = DataType.getType(this.type));
};
Aggregation.prototype.get = function(that) {
return that[this._sGetter]();
};
Aggregation.prototype.set = function(that, oValue) {
return that[this._sMutator](oValue);
};
Aggregation.prototype.add = function(that, oValue) {
return that[this._sMutator](oValue);
};
Aggregation.prototype.insert = function(that, oValue, iPos) {
return that[this._sInsertMutator](oValue, iPos);
};
Aggregation.prototype.remove = function(that, vValue) {
return that[this._sRemoveMutator](vValue);
};
Aggregation.prototype.removeAll = function(that) {
return that[this._sRemoveAllMutator]();
};
Aggregation.prototype.indexOf = function(that, oValue) {
return that[this._sIndexGetter](oValue);
};
// ---- Association -----------------------------------------------------------------------
/**
* Association info object
* @private
* @since 1.27.1
*/
function Association(oClass, name, info) {
info = typeof info !== 'object' ? { type: info } : info;
this.name = name;
this.type = info.type || 'sap.ui.core.Control';
this.multiple = info.multiple || false;
this.singularName = this.multiple ? info.singularName || guessSingularName(name) : undefined;
this.deprecated = info.deprecated || false;
this.visibility = 'public';
this.appData = remainder(this, info);
this._oParent = oClass;
this._sUID = 'association:' + name;
this._iKind = this.multiple ? Kind.MULTIPLE_ASSOCIATION : Kind.SINGLE_ASSOCIATION;
var N = capitalize(name);
this._sGetter = 'get' + N;
if ( this.multiple ) {
var N1 = capitalize(this.singularName);
this._sMutator = 'add' + N1;
this._sRemoveMutator = 'remove' + N1;
this._sRemoveAllMutator = 'removeAll' + N1;
} else {
this._sMutator = 'set' + N;
this._sRemoveMutator =
this._sRemoveAllMutator = undefined;
}
}
/**
* @private
*/
Association.prototype.generate = function(add) {
var that = this,
n = that.name;
if ( !that.multiple ) {
add(that._sGetter, function() { return this.getAssociation(n); });
add(that._sMutator, function(v) { this.setAssociation(n,v); return this; }, that);
} else {
add(that._sGetter, function() { return this.getAssociation(n,[]); });
add(that._sMutator, function(a) { this.addAssociation(n,a); return this; }, that);
add(that._sRemoveMutator, function(a) { return this.removeAssociation(n,a); });
add(that._sRemoveAllMutator, function() { return this.removeAllAssociation(n); });
}
};
Association.prototype.getType = function() {
return this._oType || (this._oType = DataType.getType(this.type));
};
Association.prototype.get = function(that) {
return that[this._sGetter]();
};
Association.prototype.set = function(that, oValue) {
return that[this._sMutator](oValue);
};
Association.prototype.remove = function(that, vValue) {
return that[this._sRemoveMutator](vValue);
};
Association.prototype.removeAll = function(that) {
return that[this._sRemoveAllMutator]();
};
// ---- Event -----------------------------------------------------------------------------
/**
* Event info object
* @private
* @since 1.27.1
*/
function Event(oClass, name, info) {
this.name = name;
this.allowPreventDefault = info.allowPreventDefault || false;
this.deprecated = info.deprecated || false;
this.visibility = 'public';
this.allowPreventDefault = !!info.allowPreventDefault;
this.enableEventBubbling = !!info.enableEventBubbling;
this.appData = remainder(this, info);
this._oParent = oClass;
this._sUID = 'event:' + name;
this._iKind = Kind.EVENT;
var N = capitalize(name);
this._sMutator = 'attach' + N;
this._sDetachMutator = 'detach' + N;
this._sTrigger = 'fire' + N;
}
/**
* @private
*/
Event.prototype.generate = function(add) {
var that = this,
n = that.name,
allowPreventDefault = that.allowPreventDefault,
enableEventBubbling = that.enableEventBubbling;
add(that._sMutator, function(d,f,o) { this.attachEvent(n,d,f,o); return this; }, that);
add(that._sDetachMutator, function(f,o) { this.detachEvent(n,f,o); return this; });
add(that._sTrigger, function(p) { return this.fireEvent(n,p, allowPreventDefault, enableEventBubbling); });
};
Event.prototype.attach = function(that,data,fn,listener) {
return that[this._sMutator](data,fn,listener);
};
Event.prototype.detach = function(that,fn,listener) {
return that[this._sDetachMutator](fn,listener);
};
Event.prototype.fire = function(that,params, allowPreventDefault, enableEventBubbling) {
return that[this._sTrigger](params, allowPreventDefault, enableEventBubbling);
};
// ----------------------------------------------------------------------------------------
ManagedObjectMetadata.prototype.metaFactorySpecialSetting = SpecialSetting;
ManagedObjectMetadata.prototype.metaFactoryProperty = Property;
ManagedObjectMetadata.prototype.metaFactoryAggregation = Aggregation;
ManagedObjectMetadata.prototype.metaFactoryAssociation = Association;
ManagedObjectMetadata.prototype.metaFactoryEvent = Event;
/**
* @private
*/
ManagedObjectMetadata.prototype.applySettings = function(oClassInfo) {
var that = this,
oStaticInfo = oClassInfo.metadata;
Metadata.prototype.applySettings.call(this, oClassInfo);
function normalize(mInfoMap, FNClass) {
var mResult = {},
sName;
if ( mInfoMap ) {
for (sName in mInfoMap) {
if ( hasOwnProperty.call(mInfoMap, sName) ) {
mResult[sName] = new FNClass(that, sName, mInfoMap[sName]);
}
}
}
return mResult;
}
function filter(mInfoMap, bPublic) {
var mResult = {},sName;
for (sName in mInfoMap) {
if ( bPublic === (mInfoMap[sName].visibility === 'public') ) {
mResult[sName] = mInfoMap[sName];
}
}
return mResult;
}
var rLibName = /([a-z][^.]*(?:\.[a-z][^.]*)*)\./;
function defaultLibName(sName) {
var m = rLibName.exec(sName);
return (m && m[1]) || "";
}
// init basic metadata from static infos and fallback to defaults
this._sLibraryName = oStaticInfo.library || defaultLibName(this.getName());
this._mSpecialSettings = normalize(oStaticInfo.specialSettings, this.metaFactorySpecialSetting);
this._mProperties = normalize(oStaticInfo.properties, this.metaFactoryProperty);
var mAllAggregations = normalize(oStaticInfo.aggregations, this.metaFactoryAggregation);
this._mAggregations = filter(mAllAggregations, true);
this._mPrivateAggregations = filter(mAllAggregations, false);
this._sDefaultAggregation = oStaticInfo.defaultAggregation || null;
this._mAssociations = normalize(oStaticInfo.associations, this.metaFactoryAssociation);
this._mEvents = normalize(oStaticInfo.events, this.metaFactoryEvent);
if ( oClassInfo.metadata.__version > 1.0 ) {
this.generateAccessors();
}
};
/**
* @private
*/
ManagedObjectMetadata.prototype.afterApplySettings = function() {
Metadata.prototype.afterApplySettings.call(this);
// if there is a parent class, produce the flattened "all" views for the element specific metadata
// PERFOPT: this could be done lazily
var oParent = this.getParent();
if ( oParent && oParent instanceof ManagedObjectMetadata ) {
this._mAllEvents = jQuery.extend({}, oParent._mAllEvents, this._mEvents);
this._mAllProperties = jQuery.extend({}, oParent._mAllProperties, this._mProperties);
this._mAllPrivateAggregations = jQuery.extend({}, oParent._mAllPrivateAggregations, this._mPrivateAggregations);
this._mAllAggregations = jQuery.extend({}, oParent._mAllAggregations, this._mAggregations);
this._mAllAssociations = jQuery.extend({}, oParent._mAllAssociations, this._mAssociations);
this._sDefaultAggregation = this._sDefaultAggregation || oParent._sDefaultAggregation;
if ( oParent._mHiddenAggregations ) {
this._mHiddenAggregations = jQuery.extend({}, oParent._mHiddenAggregations);
}
this._mAllSpecialSettings = jQuery.extend({}, oParent._mAllSpecialSettings, this._mSpecialSettings);
} else {
this._mAllEvents = this._mEvents;
this._mAllProperties = this._mProperties;
this._mAllPrivateAggregations = this._mPrivateAggregations;
this._mAllAggregations = this._mAggregations;
this._mAllAssociations = this._mAssociations;
this._mAllSpecialSettings = this._mSpecialSettings;
}
};
ManagedObjectMetadata.Kind = Kind;
/**
* Returns the name of the library that contains the described UIElement.
* @return {string} the name of the library
* @public
*/
ManagedObjectMetadata.prototype.getLibraryName = function() {
return this._sLibraryName;
};
// ---- properties ------------------------------------------------------------------------
/**
* Declares an additional property for the described class.
*
* Any property declaration via this method must happen before the described class
* is subclassed, or the added property will not be visible in the subclass.
*
* Typically used to enrich UIElement classes in an aspect oriented manner.
* @param {string} sName name of the property to add
* @param {object} oInfo metadata for the property
* @public
* @see sap.ui.core.EnabledPropagator
*/
ManagedObjectMetadata.prototype.addProperty = function(sName, oInfo) {
var oProp = this._mProperties[sName] = new Property(this, sName, oInfo);
if (!this._mAllProperties[sName]) {// ensure extended AllProperties meta-data is also enriched
this._mAllProperties[sName] = oProp;
}
// TODO notify listeners (subclasses) about change
};
/**
* Checks the existence of the given property by its name
* @param {string} sName name of the property
* @return {boolean} true, if the property exists
* @public
*/
ManagedObjectMetadata.prototype.hasProperty = function(sName) {
return !!this._mAllProperties[sName];
};
/**
* Returns an info object for the named public property of the described class,
* no matter whether the property was defined by the class itself or by one of its
* ancestor classes.
*
* If neither the described class nor its ancestor classes define a property with the
* given name, <code>undefined</code> is returned.
*
* @param {string} sName name of the property
* @returns {Object} An info object describing the property or <code>undefined</code>
* @public
* @since 1.27.0
* @experimental Type, structure and behavior of the returned info object is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getProperty = function(sName) {
var oProp = this._mAllProperties[sName];
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return typeof oProp === 'object' ? oProp : undefined;
};
/**
* Returns a map of info objects for the public properties of the described class.
* Properties declared by ancestor classes are not included.
*
* The returned map keys the property info objects by their name.
*
* @return {map} Map of property info objects keyed by the property names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getProperties = function() {
return this._mProperties;
};
/**
* Returns a map of info objects for all public properties of the described class,
* including public properties from the ancestor classes.
*
* The returned map keys the property info objects by their name.
*
* @return {map} Map of property info objects keyed by the property names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAllProperties = function() {
return this._mAllProperties;
};
// ---- aggregations ----------------------------------------------------------------------
/**
* Checks the existence of the given aggregation by its name.
* @param {string} sName name of the aggregation
* @return {boolean} true, if the aggregation exists
* @public
*/
ManagedObjectMetadata.prototype.hasAggregation = function(sName) {
return !!this._mAllAggregations[sName];
};
/**
* Returns an info object for the named public aggregation of the described class
* no matter whether the aggregation was defined by the class itself or by one of its
* ancestor classes.
*
* If neither the class nor its ancestor classes define a public aggregation with the given
* name, <code>undefined</code> is returned.
*
* If the name is not given (or has a falsy value), then it is substituted by the
* name of the default aggregation of the 'described class' (if any).
*
* @param {string} [sName] name of the aggregation or empty
* @returns {Object} An info object describing the aggregation or <code>undefined</code>
* @public
* @since 1.27.0
* @experimental Type, structure and behavior of the returned info object is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAggregation = function(sName) {
sName = sName || this._sDefaultAggregation;
var oAggr = sName ? this._mAllAggregations[sName] : undefined;
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return typeof oAggr === 'object' ? oAggr : undefined;
};
/**
* Returns a map of info objects for the public aggregations of the described class.
* Aggregations declared by ancestor classes are not included.
*
* The returned map keys the aggregation info objects by their name.
* In case of 0..1 aggregations this is the singular name, otherwise it is the plural name.
*
* @return {map} Map of aggregation info objects keyed by aggregation names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAggregations = function() {
return this._mAggregations;
};
/**
* Returns a map of info objects for all public aggregations of the described class,
* including public aggregations form the ancestor classes.
*
* The returned map keys the aggregation info objects by their name.
* In case of 0..1 aggregations this is the singular name, otherwise it is the plural
* name.
*
* @return {map} Map of aggregation info objects keyed by aggregation names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAllAggregations = function() {
return this._mAllAggregations;
};
/**
* Returns a map of info objects for all private (hidden) aggregations of the described class,
* including private aggregations from the ancestor classes.
*
* The returned map contains aggregation info objects keyed by the aggregation name.
* In case of 0..1 aggregations this is the singular name, otherwise it is the plural name.
*
* @return {map} Map of aggregation infos keyed by aggregation names
* @protected
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAllPrivateAggregations = function() {
return this._mAllPrivateAggregations;
};
/**
* Returns the info object for the named public or private aggregation declared by the
* described class or by any of its ancestors.
*
* If the name is not given (or has a falsy value), then it is substituted by the
* name of the default aggregation of the described class (if it is defined).
*
* @param {string} sAggregationName name of the aggregation to be retrieved or empty
* @return {object} aggregation info object or undefined
* @protected
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getManagedAggregation = function(sAggregationName) {
sAggregationName = sAggregationName || this._sDefaultAggregation;
var oAggr = sAggregationName ? this._mAllAggregations[sAggregationName] || this._mAllPrivateAggregations[sAggregationName] : undefined;
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return typeof oAggr === 'object' ? oAggr : undefined;
};
/**
* Returns the name of the default aggregation of the described class.
*
* If the class itself does not define a default aggregation, then the default aggregation
* of the parent is returned. If no class in the hierarchy defines a default aggregation,
* <code>undefined</code> is returned.
*
* @return {string} Name of the default aggregation
*/
ManagedObjectMetadata.prototype.getDefaultAggregationName = function() {
return this._sDefaultAggregation;
};
/**
* Returns an info object for the default aggregation of the described class.
*
* If the class itself does not define a default aggregation, then the
* info object for the default aggregation of the parent class is returned.
*
* @return {Object} An info object for the default aggregation
*/
ManagedObjectMetadata.prototype.getDefaultAggregation = function() {
return this.getAggregation();
};
/**
* Returns an info object for a public setting with the given name that either is
* a managed property or a managed aggregation of cardinality 0..1 and with at least
* one simple alternative type. The setting can be defined by the class itself or
* by one of its ancestor classes.
*
* If neither the described class nor its ancestor classes define a suitable setting
* with the given name, <code>undefined</code> is returned.
*
* @param {string} sName name of the property like setting
* @returns {Object} An info object describing the property or aggregation or <code>undefined</code>
* @public
* @since 1.27.0
* @experimental Type, structure and behavior of the returned info object is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getPropertyLikeSetting = function(sName) {
// typeof is used as a fast (but weak) substitute for hasOwnProperty
var oProp = this._mAllProperties[sName];
if ( typeof oProp === 'object' ) {
return oProp;
}
oProp = this._mAllAggregations[sName];
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return ( typeof oProp === 'object' && oProp.altTypes && oProp.altTypes.length > 0 ) ? oProp : undefined;
};
// ---- associations ----------------------------------------------------------------------
/**
* Checks the existence of the given association by its name
* @param {string} sName name of the association
* @return {boolean} true, if the association exists
* @public
*/
ManagedObjectMetadata.prototype.hasAssociation = function(sName) {
return !!this._mAllAssociations[sName];
};
/**
* Returns an info object for the named public association of the described class,
* no matter whether the association was defined by the class itself or by one of its
* ancestor classes.
*
* If neither the described class nor its ancestor classes define an association with
* the given name, <code>undefined</code> is returned.
*
* @param {string} sName name of the association
* @returns {Object} An info object describing the association or <code>undefined</code>
* @public
* @since 1.27.0
* @experimental Type, structure and behavior of the returned info object is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAssociation = function(sName) {
var oAssoc = this._mAllAssociations[sName];
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return typeof oAssoc === 'object' ? oAssoc : undefined;
};
/**
* Returns a map of info objects for all public associations of the described class.
* Associations declared by ancestor classes are not included.
*
* The returned map keys the association info objects by their name.
* In case of 0..1 associations this is the singular name, otherwise it is the plural name.
*
* @return {map} Map of association info objects keyed by association names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAssociations = function() {
return this._mAssociations;
};
/**
* Returns a map of info objects for all public associations of the described class,
* including public associations form the ancestor classes.
*
* The returned map keys the association info objects by their name.
* In case of 0..1 associations this is the singular name, otherwise it is the plural name.
*
* @return {map} Map of association info objects keyed by association names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAllAssociations = function() {
return this._mAllAssociations;
};
// ---- events ----------------------------------------------------------------------------
/**
* Checks the existence of the given event by its name
*
* @param {string} sName name of the event
* @return {boolean} true, if the event exists
* @public
*/
ManagedObjectMetadata.prototype.hasEvent = function(sName) {
return !!this._mAllEvents[sName];
};
/**
* Returns an info object for the named public event of the described class,
* no matter whether the event was defined by the class itself or by one of its
* ancestor classes.
*
* If neither the described class nor its ancestor classes define an event with the
* given name, <code>undefined</code> is returned.
*
* @param {string} sName name of the event
* @returns {Object} An info object describing the event or <code>undefined</code>
* @public
* @since 1.27.0
* @experimental Type, structure and behavior of the returned info object is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getEvent = function(sName) {
var oEvent = this._mAllEvents[sName];
// typeof is used as a fast (but weak) substitute for hasOwnProperty
return typeof oEvent === 'object' ? oEvent : undefined;
};
/**
* Returns a map of info objects for the public events of the described class.
* Events declared by ancestor classes are not included.
*
* The returned map keys the event info objects by their name.
*
* @return {map} Map of event info objects keyed by event names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getEvents = function() {
return this._mEvents;
};
/**
* Returns a map of info objects for all public events of the described class,
* including public events form the ancestor classes.
*
* The returned map keys the event info objects by their name.
*
* @return {map} Map of event info objects keyed by event names
* @public
* @experimental Type, structure and behavior of the returned info objects is not documented
* and therefore not part of the API. See the {@link #constructor Notes about Info objects}
* in the constructor documentation of this class.
*/
ManagedObjectMetadata.prototype.getAllEvents = function() {
return this._mAllEvents;
};
// ---- special settings ------------------------------------------------------------------
/**
* Checks the existence of the given special setting.
* Special settings are settings that are accepted in the mSettings
* object at construction time or in an {@link sap.ui.base.ManagedObject.applySettings}
* call but that are neither properties, aggregations, associations nor events.
*
* @param {string} sName name of the settings
* @return {boolean} true, if the special setting exists
* @private
* @experimental Since 1.27.0
*/
ManagedObjectMetadata.prototype.hasSpecialSetting = function (sName) {
return !!this._mAllSpecialSettings[sName];
};
// ----------------------------------------------------------------------------------------
/**
* Returns a map of default values for all properties declared by the
* described class and its ancestors, keyed by the property name.
*
* @return {map} Map of default values keyed by property names
* @public
*/
ManagedObjectMetadata.prototype.getPropertyDefaults = function() {
var mDefaults = this._mDefaults,
oType;
if ( mDefaults ) {
return mDefaults;
}
if ( this.getParent() instanceof ManagedObjectMetadata ) {
mDefaults = jQuery.sap.newObject(this.getParent().getPropertyDefaults());
} else {
mDefaults = {};
}
for (var s in this._mProperties) {
if ( this._mProperties[s].defaultValue !== null ) {
mDefaults[s] = this._mProperties[s].defaultValue;
} else {
oType = DataType.getType(this._mProperties[s].type);
if (oType instanceof DataType) {
mDefaults[s] = oType.getDefaultValue();
} else { // Enumeration
for (var i in oType) {
mDefaults[s] = oType[i];
break;
}
}
}
}
this._mDefaults = mDefaults;
return mDefaults;
};
ManagedObjectMetadata.prototype.createPropertyBag = function() {
if ( !this._fnPropertyBagFactory ) {
this._fnPropertyBagFactory = jQuery.sap.factory(this.getPropertyDefaults());
}
return new (this._fnPropertyBagFactory)();
};
/**
* Helper method that enriches the (generated) information objects for children
* (e.g. properties, aggregations, ...) of this Element.
*
* Also ensures that the parent metadata is enriched.
*
* @private
*/
ManagedObjectMetadata.prototype._enrichChildInfos = function() {
jQuery.sap.log.error("obsolete call to ManagedObjectMetadata._enrichChildInfos. This private method will be deleted soon");
};
/**
* Returns a map with all settings of the described class..
* Mainly used for the {@link sap.ui.base.ManagedObject#applySettings} method.
*
* @see sap.ui.base.ManagedObject#applySettings
* @private
*/
ManagedObjectMetadata.prototype.getJSONKeys = function() {
if ( this._mJSONKeys ) {
return this._mJSONKeys;
}
var mAllSettings = {},
mJSONKeys = {};
function addKeys(m) {
var sName, oInfo, oPrevInfo;
for (sName in m) {
oInfo = m[sName];
oPrevInfo = mAllSettings[sName];
if ( !oPrevInfo || oInfo._iKind < oPrevInfo._iKind ) {
mAllSettings[sName] = mJSONKeys[sName] = oInfo;
}
mJSONKeys[oInfo._sUID] = oInfo;
}
}
addKeys(this._mAllSpecialSettings);
addKeys(this.getAllProperties());
addKeys(this.getAllAggregations());
addKeys(this.getAllAssociations());
addKeys(this.getAllEvents());
this._mJSONKeys = mJSONKeys;
this._mAllSettings = mAllSettings;
return this._mJSONKeys;
};
/**
* @private
*/
ManagedObjectMetadata.prototype.getAllSettings = function() {
if ( !this._mAllSettings ) {
this.getJSONKeys();
}
return this._mAllSettings;
};
/**
* Filter out settings from the given map that are not described in the metadata.
* If null or undefined is given, null or undefined is returned.
*
* @param {object} mSettings original filters or null
* @returns {object} filtered settings or null
* @private
* @since 1.27.0
*/
ManagedObjectMetadata.prototype.removeUnknownSettings = function(mSettings) {
jQuery.sap.assert(mSettings == null || typeof mSettings === 'object', "mSettings must be null or an object");
if ( mSettings == null ) {
return mSettings;
}
var mValidKeys = this.getJSONKeys(),
mResult = {},
sName;
for ( sName in mSettings ) {
if ( hasOwnProperty.call(mValidKeys, sName) ) {
mResult[sName] = mSettings[sName];
}
}
return mResult;
};
ManagedObjectMetadata.prototype.generateAccessors = function() {
var proto = this.getClass().prototype,
prefix = this.getName() + ".",
methods = this._aPublicMethods,
n;
function add(name, fn, info) {
if ( !proto[name] ) {
proto[name] = (info && info.deprecated) ? deprecation(fn, prefix + info.name) : fn;
}
methods.push(name);
}
for (n in this._mProperties) {
this._mProperties[n].generate(add);
}
for (n in this._mAggregations) {
this._mAggregations[n].generate(add);
}
for (n in this._mAssociations) {
this._mAssociations[n].generate(add);
}
for (n in this._mEvents) {
this._mEvents[n].generate(add);
}
};
// ---- autoid creation -------------------------------------------------------------
/**
* Usage counters for the different UID tokens
*/
var mUIDCounts = {};
function uid(sId) {
jQuery.sap.assert(!/[0-9]+$/.exec(sId), "AutoId Prefixes must not end with numbers");
sId = sap.ui.getCore().getConfiguration().getUIDPrefix() + sId;
// initialize counter
mUIDCounts[sId] = mUIDCounts[sId] || 0;
// combine prefix + counter
// concatenating sId and a counter is only safe because we don't allow trailing numbers in sId!
return (sId + mUIDCounts[sId]++);
}
/**
* Calculates a new id based on a prefix.
*
* @return {string} A (hopefully unique) control id
* @public
* @function
*/
ManagedObjectMetadata.uid = uid;
/**
* Calculates a new id for an instance of this class.
*
* Note that the calculated short name part is usually not unique across
* all classes, but doesn't have to be. It might even be empty when the
* class name consists of invalid characters only.
*
* @return {string} A (hopefully unique) control id
* @public
*/
ManagedObjectMetadata.prototype.uid = function() {
var sId = this._sUIDToken;
if ( typeof sId !== "string" ) {
// start with qualified class name
sId = this.getName();
// reduce to unqualified name
sId = sId.slice(sId.lastIndexOf('.') + 1);
// reduce a camel case, multi word name to the last word
sId = sId.replace(/([a-z])([A-Z])/g, "$1 $2").split(" ").slice(-1)[0];
// remove unwanted chars (and no trailing digits!) and convert to lower case
sId = this._sUIDToken = sId.replace(/([^A-Za-z0-9-_.:])|([0-9]+$)/g,"").toLowerCase();
}
return uid(sId);
};
/**
* Test whether a given ID looks like it was automatically generated.
*
* Examples:
* <pre>
* True for:
* "foo--__bar04--baz"
* "foo--__bar04"
* "__bar04--baz"
* "__bar04"
* "__bar04--"
* "__bar04--foo"
* False for:
* "foo__bar04"
* "foo__bar04--baz"
* </pre>
*
* @see ManagedObjectMetadata.prototype.uid for details on ID generation
* @param {string} sId the ID that should be tested
* @return {boolean} whether the ID is llikely to be generated
* @static
* @public
*/
ManagedObjectMetadata.isGeneratedId = function(sId) {
var sPrefix = jQuery.sap.escapeRegExp(sap.ui.getCore().getConfiguration().getUIDPrefix());
var rIsGenerated = new RegExp(
"(^|-{1,3})" + sPrefix
);
return rIsGenerated.test(sId);
};
return ManagedObjectMetadata;
}, /* bExport= */ true);
},
"sap/ui/base/Metadata.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.Metadata
sap.ui.define(['jquery.sap.global', 'jquery.sap.script'],
function(jQuery/* , jQuerySap */) {
"use strict";
/**
* Creates a new metadata object from the given static infos.
*
* Note: throughout this class documentation, the described subclass of Object
* is referenced as <i>the described class</i>.
*
* @param {string} sClassName fully qualified name of the described class
* @param {object} oClassInfo info to construct the class and its metadata from
*
* @class Metadata for a class.
* @author Frank Weigel
* @version 1.28.5
* @since 0.8.6
* @public
* @alias sap.ui.base.Metadata
*/
var Metadata = function(sClassName, oClassInfo) {
jQuery.sap.assert(typeof sClassName === "string" && sClassName, "Metadata: sClassName must be a non-empty string");
jQuery.sap.assert(typeof oClassInfo === "object", "Metadata: oClassInfo must be empty or an object");
// support for old usage of Metadata
if ( !oClassInfo || typeof oClassInfo.metadata !== "object" ) {
oClassInfo = {
metadata : oClassInfo || {},
// retrieve class by its name. Using a lookup costs time but avoids the need for redundant arguments to this function
constructor : jQuery.sap.getObject(sClassName)
};
oClassInfo.metadata.__version = 1.0;
}
oClassInfo.metadata.__version = oClassInfo.metadata.__version || 2.0;
if ( typeof oClassInfo.constructor !== "function" ) {
throw Error("constructor for class " + sClassName + " must have been declared before creating metadata for it");
}
// invariant: oClassInfo exists, oClassInfo.metadata exists, oClassInfo.constructor exists
this._sClassName = sClassName;
this._oClass = oClassInfo.constructor;
this.extend(oClassInfo);
};
/**
* @private
* @final
*/
Metadata.prototype.extend = function(oClassInfo) {
this.applySettings(oClassInfo);
this.afterApplySettings();
};
/**
* @private
* @since 1.3.1
*/
Metadata.prototype.applySettings = function(oClassInfo) {
var that = this,
oStaticInfo = oClassInfo.metadata,
oPrototype;
if ( oStaticInfo.baseType ) {
// lookup base class by its name - same reasoning as above
var oParentClass = jQuery.sap.getObject(oStaticInfo.baseType);
if ( typeof oParentClass !== "function" ) {
jQuery.sap.log.fatal("base class '" + oStaticInfo.baseType + "' does not exist");
}
// link metadata with base metadata
if ( oParentClass.getMetadata ) {
this._oParent = oParentClass.getMetadata();
jQuery.sap.assert(oParentClass === oParentClass.getMetadata().getClass(), "Metadata: oParentClass must match the class in the parent metadata");
} else {
// fallback, if base class has no metadata
this._oParent = new Metadata(oStaticInfo.baseType, {});
}
} else {
this._oParent = undefined;
}
this._bAbstract = !!oStaticInfo["abstract"];
this._bFinal = !!oStaticInfo["final"];
this._sStereotype = oStaticInfo.stereotype || (this._oParent ? this._oParent._sStereotype : "object");
this._bDeprecated = !!oStaticInfo["deprecated"];
// handle interfaces
this._aInterfaces = jQuery.sap.unique(oStaticInfo.interfaces || []);
// take over metadata from static info
this._aPublicMethods = jQuery.sap.unique(oStaticInfo.publicMethods || []);
// enrich prototype
oPrototype = this._oClass.prototype;
jQuery.sap.forIn(oClassInfo, function(n, v) {
if ( n !== "metadata" && n !== "constructor" ) {
oPrototype[n] = v;
if ( !n.match(/^_|^on|^init$|^exit$/) ) {
// TODO hard coded knowledge about event handlers ("on") and about init/exit hooks is not nice....
that._aPublicMethods.push(n);
}
}
});
};
/**
* Called after new settings have been applied.
*
* Typically, this method is used to do some cleanup (e.g. uniqueness)
* or to calculate an optimized version of some data.
* @private
* @since 1.3.1
*/
Metadata.prototype.afterApplySettings = function() {
// create the flattened "all" view
if ( this._oParent ) {
//this._aAllInterfaces = jQuery.sap.unique(this._oParent._aAllInterfaces.concat(this._aInterfaces));
this._aAllPublicMethods = jQuery.sap.unique(this._oParent._aAllPublicMethods.concat(this._aPublicMethods));
} else {
//this._aAllInterfaces = this._aInterfaces;
this._aAllPublicMethods = this._aPublicMethods;
}
};
/**
* Stereotype of the described class.
* @experimental might be enhanced to a set of stereotypes
*/
Metadata.prototype.getStereotype = function() {
return this._sStereotype;
};
/**
* Returns the fully qualified name of the described class
* @return {string} name of the described class
* @public
*/
Metadata.prototype.getName = function() {
return this._sClassName;
};
/**
* Returns the (constructor of the) described class
* @return {function} class described by this metadata
* @public
*/
Metadata.prototype.getClass = function() {
return this._oClass;
};
/**
* Returns the metadata object of the base class of the described class
* or null if the class has no (documented) base class.
*
* @return {sap.ui.base.Metadata} metadata of the base class
* @public
*/
Metadata.prototype.getParent = function() {
return this._oParent;
};
/**
* Returns an array with the names of the public methods declared by the described class.
*
* @return {string[]} array with names of public methods declared by the described class
* @public
*/
Metadata.prototype.getPublicMethods = function() {
return this._aPublicMethods;
};
/**
* Returns an array with the names of all public methods declared by the described class
* and its ancestors.
*
* @return {string[]} array with names of all public methods provided by the described class and its ancestors
* @public
*/
Metadata.prototype.getAllPublicMethods = function() {
return this._aAllPublicMethods;
};
/**
* Returns the names of interfaces implemented by the described class.
* As the representation of interfaces is not clear yet, this method is still private.
*
* @return {string} array of names of implemented interfaces
* @private
*/
Metadata.prototype.getInterfaces = function() {
return this._aInterfaces;
};
/**
* Checks whether the described class or one of its ancestor classes implements the given interface.
*
* @param {string} sInterface name of the interface to test for (in dot notation)
* @return {boolean} whether this class implements the interface
* @public
*/
Metadata.prototype.isInstanceOf = function(sInterface) {
if ( this._oParent ) {
if ( this._oParent.isInstanceOf(sInterface) ) {
return true;
}
}
var a = this._aInterfaces;
for (var i = 0,l = a.length; i < l; i++) {
// FIXME doesn't handle interface inheritance (requires object representation for interfaces)
if ( a[i] === sInterface ) {
return true;
}
}
return false;
};
/**
* Returns whether the described class is abstract
* @return {boolean} whether the class is abstract
* @public
*/
Metadata.prototype.isAbstract = function() {
return this._bAbstract;
};
/**
* Returns whether the described class is final
* @return {boolean} whether the class is final
* @public
*/
Metadata.prototype.isFinal = function() {
return this._bFinal;
};
/**
* Whether the described class is deprecated and should not be used any more
*
* @return {boolean} whether the class is considered deprecated
* @public
* @since 1.26.4
*/
Metadata.prototype.isDeprecated = function() {
return this._bDeprecated;
};
/**
* Adds one or more new methods to the list of API methods.
*
* Can be used by contributer classes (like the EnabledPropagator) to enrich the declared set of methods.
* The method can either be called with multiple names (strings) or with one array of strings.
*
* <b>Note</b>: the newly added method(s) will only be visible in {@link sap.ui.base.Interface interface}
* objects that are created <i>after</i> this method has been called.
*
* @param {string|string[]} sMethod name(s) of the new method(s)
*/
Metadata.prototype.addPublicMethods = function(sMethod /* ... */) {
var aNames = (sMethod instanceof Array) ? sMethod : arguments;
function upush(a,v) {
Array.prototype.push.apply(a, v); // appends "inplace"
jQuery.sap.unique(a);
}
upush(this._aPublicMethods, aNames);
upush(this._aAllPublicMethods, aNames);
};
/**
* @since 1.3.1
* @private
*/
Metadata.createClass = function (fnBaseClass, sClassName, oClassInfo, FNMetaImpl) {
if ( typeof fnBaseClass === "string" ) {
FNMetaImpl = oClassInfo;
oClassInfo = sClassName;
sClassName = fnBaseClass;
fnBaseClass = null;
}
jQuery.sap.assert(!fnBaseClass || typeof fnBaseClass === "function");
jQuery.sap.assert(typeof sClassName === "string" && !!sClassName);
jQuery.sap.assert(!oClassInfo || typeof oClassInfo === "object");
jQuery.sap.assert(!FNMetaImpl || typeof FNMetaImpl === "function");
// allow metadata class to preprocess
FNMetaImpl = FNMetaImpl || Metadata;
if ( typeof FNMetaImpl.preprocessClassInfo === "function" ) {
oClassInfo = FNMetaImpl.preprocessClassInfo(oClassInfo);
}
// normalize oClassInfo
oClassInfo = oClassInfo || {};
oClassInfo.metadata = oClassInfo.metadata || {};
if ( !oClassInfo.hasOwnProperty('constructor') ) {
oClassInfo.constructor = undefined;
}
var fnClass = oClassInfo.constructor;
jQuery.sap.assert(!fnClass || typeof fnClass === "function");
// ensure defaults
if ( fnBaseClass ) {
// default constructor just delegates to base class
if ( !fnClass ) {
if ( oClassInfo.metadata.deprecated ) {
// create default factory with deprecation warning
fnClass = function() {
jQuery.sap.log.warning("Usage of deprecated class: " + sClassName);
fnBaseClass.apply(this, arguments);
};
} else {
// create default factory
fnClass = function() {
fnBaseClass.apply(this, arguments);
};
}
}
// create prototype chain
fnClass.prototype = jQuery.sap.newObject(fnBaseClass.prototype);
fnClass.prototype.constructor = fnClass;
// enforce correct baseType
oClassInfo.metadata.baseType = fnBaseClass.getMetadata().getName();
} else {
// default constructor does nothing
fnClass = fnClass || function() { };
// enforce correct baseType
delete oClassInfo.metadata.baseType;
}
oClassInfo.constructor = fnClass;
// make the class visible as JS Object
jQuery.sap.setObject(sClassName, fnClass);
// add metadata
var oMetadata = new FNMetaImpl(sClassName, oClassInfo);
fnClass.getMetadata = fnClass.prototype.getMetadata = jQuery.sap.getter(oMetadata);
// enrich function
if ( !fnClass.getMetadata().isFinal() ) {
fnClass.extend = function(sSCName, oSCClassInfo, fnSCMetaImpl) {
return Metadata.createClass(fnClass, sSCName, oSCClassInfo, fnSCMetaImpl || FNMetaImpl);
};
}
return fnClass;
};
return Metadata;
}, /* bExport= */ true);
},
"sap/ui/base/Object.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/**
* SAPUI5 base classes
*
* @namespace
* @name sap.ui.base
* @public
*/
// Provides class sap.ui.base.Object
sap.ui.define(['jquery.sap.global', './Interface', './Metadata'],
function(jQuery, Interface, Metadata) {
"use strict";
/**
* Constructor for a sap.ui.base.Object.
*
* @class Base class for all SAPUI5 Objects
* @abstract
* @author Malte Wedel
* @version 1.28.5
* @public
* @alias sap.ui.base.Object
*/
var BaseObject = Metadata.createClass("sap.ui.base.Object", {
constructor : function() {
// complain if 'this' is not an instance of a subclass
if ( !(this instanceof BaseObject) ) {
throw Error("Cannot instantiate object: \"new\" is missing!");
}
}
});
/**
* Destructor method for objects
* @public
*/
BaseObject.prototype.destroy = function() {
};
/**
* Returns the public interface of the object.
*
* @return {sap.ui.base.Interface} the public interface of the object
* @public
*/
BaseObject.prototype.getInterface = function() {
// New implementation that avoids the overhead of a dedicated member for the interface
// initially, an Object instance has no associated Interface and the getInterface
// method is defined only in the prototype. So the code here will be executed.
// It creates an interface (basically the same code as in the old implementation)
var oInterface = new Interface(this, this.getMetadata().getAllPublicMethods());
// Now this Object instance gets a new, private implementation of getInterface
// that returns the newly created oInterface. Future calls of getInterface on the
// same Object therefore will return the already created interface
this.getInterface = jQuery.sap.getter(oInterface);
// as the first caller doesn't benefit from the new method implementation we have to
// return the created interface as well.
return oInterface;
};
/**
* Returns the metadata for the class that this object belongs to.
*
* This method is only defined when metadata has been declared by using {@link sap.ui.base.Object.defineClass}
* or {@link sap.ui.base.Object.extend}.
*
* @return {sap.ui.base.Metadata] metadata for the class of the object
* @name sap.ui.base.Object#getMetadata
* @function
* @public
*/
/**
* Creates a subclass of class sap.ui.base.Object with name <code>sClassName</code>
* and enriches it with the information contained in <code>oClassInfo</code>.
*
* <code>oClassInfo</code> might contain three kinds of informations:
* <ul>
* <li><code>metadata:</code> an (optional) object literal with metadata about the class.
* The information in the object literal will be wrapped by an instance of {@link sap.ui.base.Metadata Metadata}
* and might contain the following information
* <ul>
* <li><code>interfaces:</code> {string[]} (optional) set of names of implemented interfaces (defaults to no interfaces)</li>
* <li><code>publicMethods:</code> {string[]} (optional) list of methods that should be part of the public
* facade of the class</li>
* <li><code>abstract:</code> {boolean} (optional) flag that marks the class as abstract (purely informational, defaults to false)</li>
* <li><code>final:</code> {boolean} (optional) flag that marks the class as final (defaults to false)</li>
* </ul>
* Subclasses of sap.ui.base.Object can enrich the set of supported metadata (e.g. see {@link sap.ui.core.Element.extend}).
* </li>
*
* <li><code>constructor:</code> a function that serves as a constructor function for the new class.
* If no constructor function is given, the framework creates a default implementation that delegates all
* its arguments to the constructor function of the base class.
* </li>
*
* <li><i>any-other-name:</i> any other property in the <code>oClassInfo</code> is copied into the prototype
* object of the newly created class. Callers can thereby add methods or properties to all instances of the
* class. But be aware that the given values are shared between all instances of the class. Usually, it doesn't
* make sense to use primitive values here other than to declare public constants.
* </li>
*
* </ul>
*
* The prototype object of the newly created class uses the same prototype as instances of the base class
* (prototype chaining).
*
* A metadata object is always created, even if there is no <code>metadata</code> entry in the <code>oClassInfo</code>
* object. A getter for the metadata is always attached to the prototype and to the class (constructor function)
* itself.
*
* Last but not least, with the third argument <code>FNMetaImpl</code> the constructor of a metadata class
* can be specified. Instances of that class will be used to represent metadata for the newly created class
* and for any subclass created from it. Typically, only frameworks will use this parameter to enrich the
* metadata for a new class hierarchy they introduce (e.g. {@link sap.ui.core.Element.extend Element}).
*
* @param {string} sClassName name of the class to be created
* @param {object} [oClassInfo] structured object with informations about the class
* @param {function} [FNMetaImpl] constructor function for the metadata object. If not given, it defaults to sap.ui.base.Metadata.
* @return {function} the created class / constructor function
* @public
* @static
* @name sap.ui.base.Object.extend
* @function
* @since 1.3.1
*/
/**
* Creates metadata for a given class and attaches it to the constructor and prototype of that class.
*
* After creation, metadata can be retrieved with getMetadata().
*
* The static info can at least contain the following entries:
* <ul>
* <li>baseType: {string} fully qualified name of a base class or empty
* <li>publicMethods: {string} an array of method names that will be visible in the interface proxy returned by {@link #getInterface}
* </ul>
*
* @param {string} sClassName name of an (already declared) constructor function
* @param {object} oStaticInfo static info used to create the metadata object
* @param {string} oStaticInfo.baseType qualified name of a base class
* @param {string[]} oStaticInfo.publicMethods array of names of public methods
* @param {function} [FNMetaImpl] constructor function for the metadata object. If not given, it defaults to sap.ui.base.Metadata.
*
* @return {sap.ui.base.Metadata} the created metadata object
* @public
* @static
* @deprecated Since 1.3.1. Use the static <code>extend</code> method of the desired base class (e.g. {@link sap.ui.base.Object.extend})
*/
BaseObject.defineClass = function(sClassName, oStaticInfo, FNMetaImpl) {
// create Metadata object
var oMetadata = new (FNMetaImpl || Metadata)(sClassName, oStaticInfo);
var fnClass = oMetadata.getClass();
fnClass.getMetadata = fnClass.prototype.getMetadata = jQuery.sap.getter(oMetadata);
// enrich function
if ( !oMetadata.isFinal() ) {
fnClass.extend = function(sSCName, oSCClassInfo, fnSCMetaImpl) {
return Metadata.createClass(fnClass, sSCName, oSCClassInfo, fnSCMetaImpl || FNMetaImpl);
};
}
jQuery.sap.log.debug("defined class '" + sClassName + "'" + (oMetadata.getParent() ? " as subclass of " + oMetadata.getParent().getName() : "") );
return oMetadata;
};
return BaseObject;
}, /* bExport= */ true);
},
"sap/ui/base/ObjectPool.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.base.ObjectPool
sap.ui.define(['jquery.sap.global', './Object'],
function(jQuery, BaseObject) {
"use strict";
/**
* Creates an ObjectPool instance based on the given oObjectClass.&lt;br/&gt;
* If there is a free pooled instance, returns that one, otherwise creates a new one.&lt;br/&gt;
* In order to be maintained by the ObjectPool, oObjectClass must implement
* methods described in the class description.
*
* @param {function} oObjectClass constructor for the class of objects that this pool should manage
*
* @class Manages a pool of objects all of the same type;
* the type has to be specified at pool construction time.
*
* Maintains a list of free objects of the given type.
* If {@link sap.ui.base.ObjectPool.prototype.borrowObject} is called, an existing free object
* is taken from the pool and the <code>init</code> method is called on this
* object.
*
* When no longer needed, any borrowed object should be returned to
* the pool by calling {@link #returnObject}. At that point in time,
* the reset method is called on the object and the object is added to the
* list of free objects.
*
* See {@link sap.ui.base.Poolable} for a description of the contract for poolable objects.
*
* Example:
* <pre>
* this.oEventPool = new sap.ui.base.ObjectPool(sap.ui.base.Event);
* var oEvent = this.oEventPool.borrowObject(iEventId, mParameters);
* </pre>
*
* @extends sap.ui.base.Object
* @author Malte Wedel
* @version 1.28.5
* @constructor
* @alias sap.ui.base.ObjectPool
* @public
*/
var ObjectPool = BaseObject.extend("sap.ui.base.ObjectPool", /** @lends sap.ui.base.ObjectPool.prototype */ {
constructor: function(oObjectClass) {
BaseObject.call(this);
this.oObjectClass = oObjectClass;
this.aFreeObjects = [];
// this.aUsedObjects = []; //PERFOPT: Holding those is currently senseless.
}
});
/**
* Borrows a free object from the pool. Any arguments to this method
* are forwarded to the init method of the borrowed object.
*
* @param {any} [any] optional initialization parameters for the borrowed object
* @return {object} the borrowed object of the same type that has been specified for this pool
* @public
*/
ObjectPool.prototype.borrowObject = function() {
// PERFOPT: Reduced callstack
var oObject = this.aFreeObjects.length == 0 ?
new this.oObjectClass() :
this.aFreeObjects.pop();
oObject.init.apply(oObject, arguments);
// this.aUsedObjects.push(oObject); //PERFOPT: Holding those is currently senseless.
return oObject;
};
/**
* Returns an object to the pool. The object must have been borrowed from this
* pool beforehand. The reset method is called on the object before it is added
* to the set of free objects.
*
* @param {object} oObject the object to return to the pool
* @public
*/
ObjectPool.prototype.returnObject = function(oObject) {
oObject.reset();
// If the next line is ever activated again, ensure not simply the topmost object is poped but the one returned!!
// this.aUsedObjects.pop(); //PERFOPT: Holding those is currently senseless.
this.aFreeObjects.push(oObject);
};
/**
* Poolable objects must provide a no-arg constructor which is used by the pool
* to construct new, unused objects.
*
* To be more convenient to use, poolable objects should implement their constructor
* in a way that it either can be called with no arguments (used by the pool) or
* with the same signature as their {@link #init} method (to be used by applications).
*
* @name sap.ui.base.Poolable
* @interface Contract for objects that can be pooled by ObjectPool
* @public
*/
/**
* Called by the object pool when this instance will be actived for a caller.
* The same method will be called after a new instance has been created by an otherwise
* exhausted pool.
*
* If the caller provided any arguments to {@link sap.ui.base.ObjectPool#borrowObject}
* all arguments will be propagated to this method.
*
* @name sap.ui.base.Poolable.prototype.init
* @function
* @public
*/
/**
* Called by the object pool when an instance is returned to the pool.
* While no specific implementation is required, poolable objects in general
* should clean all caller specific state (set to null) in this method to
* avoid memory leaks and to enforce garbage collection of the caller state.
*
* @name sap.ui.base.Poolable.prototype.reset
* @function
* @public
*/
return ObjectPool;
}, /* bExport= */ true);
},
"sap/ui/core/Component.js":function(){/*
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides base class sap.ui.core.Component for all components
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObject', './ComponentMetadata', './Core'],
function(jQuery, ManagedObject, ComponentMetadata, Core) {
"use strict";
/*global Promise */
/**
* Creates and initializes a new component with the given <code>sId</code> and
* settings.
*
* The set of allowed entries in the <code>mSettings</code> object depends on
* the concrete subclass and is described there. See {@link sap.ui.core.Component}
* for a general description of this argument.
*
* @param {string}
* [sId] optional id for the new control; generated automatically if
* no non-empty id is given Note: this can be omitted, no matter
* whether <code>mSettings</code> will be given or not!
* @param {object}
* [mSettings] optional map/JSON-object with initial settings for the
* new component instance
* @public
*
* @class Base Class for Component.
* @extends sap.ui.base.ManagedObject
* @abstract
* @author SAP SE
* @version 1.28.5
* @alias sap.ui.core.Component
* @since 1.9.2
*/
var Component = ManagedObject.extend("sap.ui.core.Component", /** @lends sap.ui.core.Component.prototype */
{
constructor : function(sId, mSettings) {
ManagedObject.apply(this, arguments);
},
metadata : {
stereotype : "component",
"abstract": true,
version : "0.0",
/*enable/disable type validation by MessageManager
handleValidation: 'boolean'*/
includes : [], // css, javascript files that should be used in the component
dependencies : { // external dependencies
libs : [],
components : [],
ui5version : ""
},
config: {}, // static configuration
customizing: { // component/view customizing
/* Example:
"sap.ui.viewReplacements": {
"sap.xx.org.Main": {
viewName: "sap.xx.new.Main",
type: "XML"
}
},
"sap.ui.controllerReplacements": {
"sap.xx.org.Main": "sap.xx.new.Main"
},
"sap.ui.viewExtensions": {
"sap.xx.new.Main": {
"extensionX": {
name: "sap.xx.new.Fragment1",
type: "sap.ui.core.XMLFragment"
},
"extensionY": {
...
}
}
},
"sap.ui.viewModification": {
"sap.xx.new.Main": {
"myControlId": {
text: "{i18n_custom>mytext}"
}
}
}
*/
},
/*
properties: {
config : "any"
},
*/
library: "sap.ui.core"
}
}, /* Metadata constructor */ ComponentMetadata);
/**
* Returns the metadata for the Component class.
*
* @return {sap.ui.core.ComponentMetadata} Metadata for the Component class.
* @static
* @public
* @name sap.ui.core.ComponentMetadata.getMetadata
* @function
*/
/**
* Returns the metadata for the class that this component belongs to.
*
* @return {sap.ui.core.ComponentMetadata} Metadata for the class of the component
* @public
* @name sap.ui.core.ComponentMetadata#getMetadata
* @function
*/
/**
* Activates the customizing configuration for the given component.
* @param {string} sComponentName the name of the component to activate
* @private
* @deprecated Since 1.21.0 as it is handled by component instantiation
*/
Component.activateCustomizing = function(sComponentName) {
// noop since it will be handled by component instantiation
};
/**
* Deactivates the customizing configuration for the given component.
* @param {string} sComponentName the name of the component to activate
* @private
* @deprecated Since 1.21.0 as it is handled by component termination
*/
Component.deactivateCustomizing = function(sComponentName) {
// noop since it will be handled by component termination
};
// ---- Ownership functionality ------------------------------------------------------------
//
// Implementation note: the whole ownership functionality is now part of Component
// a) to ensure that only Components are used as owners
// b) to keep component related code out of ManagedObject as far as possible
//
// Only exception is the _sOwnerId property and its assignment in the ManagedObject
// constructor, but that doesn't require much knowledge about components
/**
* Returns the Id of the object in whose "context" the given ManagedObject has been created.
*
* For objects that are not ManagedObjects or for which the owner is unknown,
* <code>undefined</code> will be returned as owner Id.
*
* <strong>Note</strong>: Ownership for objects is only checked by the framework at the time
* when they are created. It is not checked or updated afterwards. And it can only be detected
* while the {@link sap.ui.core.Component.runAsOwner Component.runAsOwner} function is executing.
* Without further action, this is only the case while the content of an UIComponent is
* {@link sap.ui.core.UIComponent.createContent constructed} or when a
* {@link sap.ui.core.routing.Router Router} creates a new View and its content.
*
* <strong>Note</string>: This method does not guarantee that the returned owner Id belongs
* to a Component. Currently, it always does. But future versions of UI5 might introduce a
* more fine grained ownership concept, e.g. taking Views into account. Callers that
* want to deal only with components as owners, should use the following method:
* {@link sap.ui.core.Component.getOwnerComponentFor Component.getOwnerComponentFor}.
* It guarantees that the returned object (if any) will be a Component.
*
* <strong>Further note</strong> that only the Id of the owner is recorded. In rare cases,
* when the lifecycle of a ManagedObject is not bound to the lifecycle of its owner,
* (e.g. by the means of aggregations), then the owner might have been destroyed already
* whereas the ManagedObject is still alive. So even the existence of an owner Id is
* not a guarantee for the existence of the corresponding owner.
*
* @param {sap.ui.base.ManagedObject} oObject Object to retrieve the owner Id for
* @return {string} the Id of the owner or <code>undefined</code>
* @static
* @public
* @since 1.15.1
*/
Component.getOwnerIdFor = function(oObject) {
jQuery.sap.assert(oObject instanceof ManagedObject, "oObject must be given and must be a ManagedObject");
var sOwnerId = ( oObject instanceof ManagedObject ) && oObject._sOwnerId;
return sOwnerId || undefined; // no or empty id --> undefined
};
/**
* Returns the Component instance in whose "context" the given ManagedObject has been created
* or <code>undefined</code>.
*
* This is a convenience wrapper around {@link sap.ui.core.Component.getOwnerIdFor Component.getOwnerIdFor}.
* If the owner Id cannot be determined for the reasons document with <code>getOwnerForId</code>
* or when the Component for the determined Id no longer exists, <code>undefined</code>
* will be returned.
*
* @param {sap.ui.base.ManagedObject} oObject Object to retrieve the owner Component for
* @return {sap.ui.core.Component} the owner Component or <code>undefined</code>.
* @static
* @public
* @since 1.25.1
*/
Component.getOwnerComponentFor = function(oObject) {
var sOwnerId = Component.getOwnerIdFor(oObject);
return sOwnerId && sap.ui.component(sOwnerId);
};
/**
* Calls the function <code>fn</code> once and marks all ManagedObjects
* created during that call as "owned" by this Component.
*
* Nested calls of this method are supported (e.g. inside a newly created,
* nested component). The currently active owner Component will be remembered
* before executing <code>fn</code> and restored afterwards.
*
* @param {function} fn the function to execute
* @return {any} result of function <code>fn</code>
* @since 1.25.1
* @public
* @experimental
*/
Component.prototype.runAsOwner = function(fn) {
jQuery.sap.assert(typeof fn === "function", "fn must be a function");
var oldOwnerId = ManagedObject._sOwnerId;
try {
ManagedObject._sOwnerId = this.getId();
return fn.call();
} finally {
ManagedObject._sOwnerId = oldOwnerId;
}
};
// ---- ----
/**
* @see sap.ui.base.Object#getInterface
* @public
*/
Component.prototype.getInterface = function() {
return this;
};
/*
* initialize the Component and keep the component data
*/
Component.prototype._initCompositeSupport = function(mSettings) {
// registry of mock servers
this._mMockServers = {};
// register the component instance
this.getMetadata().onInitComponent();
// make user specific data available during component instantiation
this.oComponentData = mSettings && mSettings.componentData;
// static initialization
this.getMetadata().init();
// init the component models
this.initComponentModels();
// error handler (if exists)
if (this.onWindowError) {
this._fnWindowErrorHandler = jQuery.proxy(function(oEvent) {
var oError = oEvent.originalEvent;
this.onWindowError(oError.message, oError.filename, oError.lineno);
}, this);
jQuery(window).bind("error", this._fnWindowErrorHandler);
}
// before unload handler (if exists)
if (this.onWindowBeforeUnload) {
this._fnWindowBeforeUnloadHandler = jQuery.proxy(this.onWindowBeforeUnload, this);
jQuery(window).bind("beforeunload", this._fnWindowBeforeUnloadHandler);
}
// unload handler (if exists)
if (this.onWindowUnload) {
this._fnWindowUnloadHandler = jQuery.proxy(this.onWindowUnload, this);
jQuery(window).bind("unload", this._fnWindowUnloadHandler);
}
};
/*
* clean up mock server and event handlers
*/
Component.prototype.destroy = function() {
// kill the mock servers
if (this._mMockServers) {
jQuery.each(this._mMockServers, function(sName, oMockServer) {
oMockServer.stop();
});
delete this._mMockServers;
}
// remove the event handlers
if (this._fnWindowErrorHandler) {
jQuery(window).unbind("error", this._fnWindowErrorHandler);
delete this._fnWindowErrorHandler;
}
if (this._fnWindowBeforeUnloadHandler) {
jQuery(window).unbind("beforeunload", this._fnWindowBeforeUnloadHandler);
delete this._fnWindowBeforeUnloadHandler;
}
if (this._fnWindowUnloadHandler) {
jQuery(window).unbind("unload", this._fnWindowUnloadHandler);
delete this._fnWindowUnloadHandler;
}
// destroy event bus
if (this._oEventBus) {
this._oEventBus.destroy();
delete this._oEventBus;
}
// destroy the object
ManagedObject.prototype.destroy.apply(this, arguments);
//unregister for messging
sap.ui.getCore().getMessageManager().unregisterObject(this);
// unregister the component instance
this.getMetadata().onExitComponent();
};
/**
* Returns user specific data object
*
* @return {object} componentData
* @public
* @since 1.15.0
*/
Component.prototype.getComponentData = function() {
return this.oComponentData;
};
/**
* Returns the event bus of this component.
* @return {sap.ui.core.EventBus} the event bus
* @since 1.20.0
* @public
*/
Component.prototype.getEventBus = function() {
if (!this._oEventBus) {
jQuery.sap.require("sap.ui.core.EventBus");
this._oEventBus = new sap.ui.core.EventBus();
}
return this._oEventBus;
};
/**
* Initializes the component models and services.
*
* @private
*/
Component.prototype.initComponentModels = function(mModels, mServices) {
var oMetadata = this.getMetadata();
// currently the automatic creation of the models is not activated
// for the metadata version 2 - here the implementation needs to be
// adopted to take the model configuration out of the models section
// of sap.ui5 and the datasources mapping from sap.app
if (oMetadata.getMetadataVersion() === 2) {
jQuery.sap.log.debug("Skipping component model creation for manifest.json models.");
return;
}
// get the application configuration
var oModelsConfig = mModels || oMetadata.getModels(),
oServicesConfig = mServices || oMetadata.getServices();
// iterate over the model configurations and create and register the
// models base on the configuration if available
if (oModelsConfig) {
// create and start the mock server
var fnCreateMockServer = function(sName, sUri, sMetadataUrl, sMockdataBaseUrl) {
// kill the existing mock server
if (this._mMockServers[sName]) {
this._mMockServers[sName].stop();
}
// start the mock server
jQuery.sap.require("sap.ui.core.util.MockServer");
this._mMockServers[sName] = new sap.ui.core.util.MockServer({
rootUri: sUri
});
this._mMockServers[sName].simulate(sMetadataUrl, sMockdataBaseUrl);
this._mMockServers[sName].start();
};
// helper to create a model depending on the type
// TODO: models could have generic instantiation to pass the JSON object
// of the configuration directly instead of individual handling
var fnCreateModel = function(sName, oConfig) {
// extract uri and type
var sUri = oConfig.uri, sType = oConfig.type;
// require the model and instantiate it
jQuery.sap.require(sType);
var oClass = jQuery.sap.getObject(sType);
jQuery.sap.assert(oClass !== undefined, "The specified model type \"" + sType + "\" could not be found!");
// create the model and apply the configuration
var oModel;
if (sType === "sap.ui.model.resource.ResourceModel") {
oModel = new oClass({bundleUrl: sUri});
} else if (sType === "sap.ui.model.odata.ODataModel" || sType === "sap.ui.model.odata.v2.ODataModel") {
// check for a mock server configuration and start the mock server
if (oConfig.mockserver) {
fnCreateMockServer.call(this, sName, sUri, oConfig.mockserver.model, oConfig.mockserver.data);
}
// create the model
oModel = new oClass(sUri, oConfig.settings);
} else if (sType === "sap.ui.model.json.JSONModel" || sType === "sap.ui.model.xml.XMLModel") {
oModel = new oClass();
if (sUri) {
oModel.loadData(sUri);
}
} /* else {
TODO: what about custom models / analog to ODataModel & setting?
} */
// check the model to be an instance of sap.ui.model.Model
jQuery.sap.assert(oModel instanceof sap.ui.model.Model, "The specified model type \"" + sType + "\" must be an instance of sap.ui.model.Model!");
return oModel;
};
// create the models
var that = this;
jQuery.each(oModelsConfig, function(sKey, oModelConfig) {
// if the model refer to a service configuration we use the service configuration
var sService = oModelConfig.service,
oModel;
if (sService) {
var oServiceConfig = oServicesConfig[sService];
jQuery.sap.assert(oServiceConfig, "The service configuration for service \"" + sService + "\" is not available!");
oModel = fnCreateModel.call(that, sKey, oServiceConfig);
} else if (oModelConfig.type) {
oModel = fnCreateModel.call(that, sKey, oModelConfig);
}
// we apply the model to the root component if created
if (oModel) {
that.setModel(oModel, sKey || undefined);
}
});
}
};
/**
* Initializes the Component instance after creation.
*
* Applications must not call this hook method directly, it is called by the
* framework while the constructor of an Component is executed.
*
* Subclasses of Component should override this hook to implement any necessary
* initialization.
*
* @function
* @name sap.ui.core.Component.prototype.init
* @protected
*/
//sap.ui.core.Component.prototype.init = function() {};
/**
* Cleans up the component instance before destruction.
*
* Applications must not call this hook method directly, it is called by the
* framework when the element is {@link #destroy destroyed}.
*
* Subclasses of Component should override this hook to implement any necessary
* cleanup.
*
* @function
* @name sap.ui.core.Component.prototype.exit
* @protected
*/
//sap.ui.core.Component.prototype.exit = function() {};
/**
* The window before unload hook. Override this method in your Component class
* implementation, to handle cleanup before the real unload or to prompt a question
* to the user, if the component should be exited.
*
* @return {string} return a string if a prompt should be displayed to the user
* confirming closing the component (e.g. when the component is not yet saved).
* @public
* @since 1.15.1
* @name sap.ui.core.Component.prototype.onWindowBeforeUnload
* @function
*/
//onWindowBeforeUnload : function() {},
/**
* The window unload hook. Override this method in your Component class
* implementation, to handle cleanup of the component once the window
* will be unloaded (e.g. closed).
*
* @public
* @since 1.15.1
* @name sap.ui.core.Component.prototype.onWindowUnload
* @function
*/
//onWindowUnload : function() {},
/**
* The window error hook. Override this method in your Component class implementation
* to listen to unhandled errors.
*
* @param {string} sMessage The error message.
* @param {string} sFile The file where the error occurred
* @param {number} iLine The line number of the error
* @public
* @since 1.15.1
* @name sap.ui.core.Component.prototype.onError
* @function
*/
//onWindowError : null, // function(sMessage, sFile, iLine) - function not added directly as it might result in bad stack traces in older browsers
/**
* The hook which gets called when the static configuration of the component
* has been changed by some configuration extension.
*
* @param {string} sConfigKey The error message.
* @public
* @since 1.15.1
* @name sap.ui.core.Component.prototype.onConfigChange
* @function
*/
//onConfigChange : null, // function(sConfigKey)
/**
* Creates a new instance of a <code>Component</code> or returns the instance
* of an existing <code>Component</code>.
*
* If you want to lookup all an existing <code>Component</code> you can call
* this function with a component Id as parameter:
* <pre>
* var oComponent = sap.ui.component(sComponentId);
* </pre>
*
* To create a new instance of a component you pass a component configuration
* object into this function:
* <pre>
* var oComponent = sap.ui.component({
* name: "my.Component",
* url: "my/component/location",
* id: "myCompId1"
* });
* </pre>
*
* @param {string|object} vConfig the id of an existing Component or the configuration object to create the Component
* @param {string} vConfig.name the name of the Component to load
* @param {string} [vConfig.url] an alternate location from where to load the Component
* @param {object} [vConfig.componentData] initial data of the Component (@see sap.ui.core.Component#getComponentData)
* @param {string} [vConfig.id] the sId of the new Component
* @param {object} [vConfig.settings] the mSettings of the new Component
* @param {boolean} [vConfig.async=false] whether the component creation should be done asynchronously (experimental setting)
* @param {object} [vConfig.asyncHints] hints for the asynchronous loading (experimental setting)
* @param {string[]} [vConfig.asyncHints.libs] libraries that should be (pre-)loaded before the component (experimental setting)
* @param {string[]} [vConfig.asyncHints.components] components that should be (pre-)loaded before the component (experimental setting)
* @return {sap.ui.core.Component|Promise} the Component instance or a Promise in case of asynchronous loading
*
* @public
* @static
* @since 1.15.0
* @experimental Since 1.27.0. Support for asynchronous loading and the corresponding hints is still experimental
* and might be modified or removed completely again. It must not be used in productive code, except in code
* delivered by the UI5 teams. The synchronous usage of the API is not experimental and can be used without
* restrictions.
*/
sap.ui.component = function(vConfig) {
// a parameter must be given!
if (!vConfig) {
throw new Error("sap.ui.component cannot be called without parameter!");
}
// when only a string is given then this function behaves like a
// getter and returns an existing component instance
if (typeof vConfig === 'string') {
// lookup and return the component
return sap.ui.getCore().getComponent(vConfig);
}
function createInstance(oClass) {
// retrieve the required properties
var sName = vConfig.name,
sId = vConfig.id,
oComponentData = vConfig.componentData,
sController = sName + '.Component',
mSettings = vConfig.settings;
// create an instance
var oInstance = new oClass(jQuery.extend({}, mSettings, {
id: sId,
componentData: oComponentData
}));
jQuery.sap.assert(oInstance instanceof Component, "The specified component \"" + sController + "\" must be an instance of sap.ui.core.Component!");
jQuery.sap.log.info("Component instance Id = " + oInstance.getId());
/*register for messging: register if either handleValidation is set in metadata or if not set in metadata and
* set on instance
*/
var bHandleValidation = oInstance.getMetadata().handleValidation() !== undefined || vConfig.handleValidation;
if (bHandleValidation) {
//calc handleValidation for registration
if (oInstance.getMetadata().handleValidation() !== undefined) {
bHandleValidation = oInstance.getMetadata().handleValidation();
} else {
bHandleValidation = vConfig.handleValidation;
}
sap.ui.getCore().getMessageManager().registerObject(oInstance, bHandleValidation);
}
return oInstance;
}
// load the component class
var vClassOrPromise = sap.ui.component.load(vConfig, true);
if ( vConfig.async ) {
// async: instantiate component after Promise has been fulfilled with component constructor
return vClassOrPromise.then(createInstance);
} else {
// sync: constructor has been returned, instantiate component immediately
return createInstance(vClassOrPromise);
}
};
/**
* Load a component without instantiating it.
*
* Provides experimental support for loading components asynchronously by setting
* <code>oConfig.async</code> to true. In that case, the method returns a Javascript 6
* Promise that will be fulfilled with the component class after loading.
*
* Using <code>async = true</code> doesn't necessarily mean that no more synchronous loading
* occurs. Both the framework as well as component implementations might still execute
* synchronous requests. The contract for <code>async = true</code> just allows to use
* async calls.
*
* When asynchronous loading is used, additional <code>hints</code> can be provided :
* <ul>
* <li><code>oConfig.asyncHints.components : string[]</code>a list of components needed by the current component and its subcomponents
* The framework will try to preload these components (their Component-preload.js) asynchronously, errors will be ignored.
* Please note that the framework has no knowledge about whether a component provides a preload file or whether it is bundled
* in some library preload. If components are listed in the hints section, they will be preloaded.</li>
* <li><code>oConfig.asyncHints.libs : string[]</code>libraries needed by the component and its subcomponents.
* The framework will asynchronously load those libraries, if they're not loaded yet.</li>
* </ul>
*
* If components and/or libraries are listed in the hints section, all the corresponding preload files will
* be requested in parallel. The constructor class will only be required after all of them are rejected or resolved.
*
* Note: so far, only the requests for the preload files (library and/or component) are executed asynchronously.
* If a preload is deactivated by configuration (e.g. debug mode), then requests won't be asynchronous.
*
* @param {object} oConfig a configuration object describing the component to be loaded. See {@link sap.ui.component} for more Information.
* @return {function|Promise} the constructor of the component class or a Promise that will be fulfilled with the same
*
* @since 1.16.3
* @static
* @public
* @experimental Since 1.27.0. Support for asynchronous loading and the corresponding hints is still experimental
* and might be modified or removed completely again. It must not be used in productive code, except in code
* delivered by the UI5 teams. The synchronous usage of the API is not experimental and can be used without
* restrictions.
*/
sap.ui.component.load = function(oConfig, bFailOnError) {
var sName = oConfig.name,
sUrl = oConfig.url,
bComponentPreload = /^(sync|async)$/.test(sap.ui.getCore().getConfiguration().getComponentPreload());
// check for an existing name
if (!sName) {
throw new Error("The name of the component is undefined.");
}
// check the type of the name
jQuery.sap.assert(typeof sName === 'string', "sName must be a string");
// if a URL is given we register this URL for the name of the component:
// the name is the package in which the component is located (dot separated)
if (sUrl) {
jQuery.sap.registerModulePath(sName, sUrl);
}
function getControllerClass() {
var sController = sName + '.Component';
// require the component controller
jQuery.sap.require(sController);
var oClass = jQuery.sap.getObject(sController);
if (!oClass) {
var sMsg = "The specified component controller '" + sController + "' could not be found!";
if (bFailOnError) {
throw new Error(sMsg);
} else {
jQuery.sap.log.warning(sMsg);
}
}
return oClass;
}
function preload(sComponentName, bAsync) {
var sController = sComponentName + '.Component',
sPreloadName;
// only load the Component-preload file if the Component module is not yet available
if ( bComponentPreload && !jQuery.sap.isDeclared(sController, /* bIncludePreloaded=*/ true) ) {
if ( bAsync ) {
sPreloadName = jQuery.sap.getResourceName(sController, '-preload.js'); // URN
return jQuery.sap._loadJSResourceAsync(sPreloadName, true);
}
try {
sPreloadName = sController + '-preload'; // Module name
jQuery.sap.require(sPreloadName);
} catch (e) {
jQuery.sap.log.warning("couldn't preload component from " + sPreloadName + ": " + ((e && e.message) || e));
}
}
}
if ( oConfig.async ) {
// trigger loading of libraries and component preloads and collect the given promises
var hints = oConfig.asyncHints || {},
promises = [],
collect = function(oPromise) {
if ( oPromise ) {
promises.push(oPromise);
}
};
// preload required libraries
if ( hints.libs ) {
collect(sap.ui.getCore().loadLibraries( hints.libs ));
}
if ( bComponentPreload ) {
collect(preload(sName, true));
// if a hint about "used" components is given, preload those components
if ( hints.components ) {
jQuery.each(hints.components, function(i, sCompName) {
collect(preload(sCompName, true));
});
}
}
// combine given promises
return Promise.all(promises).then(function(v) {
jQuery.sap.log.debug("Component.load: all promises fulfilled, then " + v);
return getControllerClass();
});
}
preload(sName);
return getControllerClass();
};
return Component;
}, /* bExport= */ true);
},
"sap/ui/core/ComponentMetadata.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.core.ComponentMetadata
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObjectMetadata'],
function(jQuery, ManagedObjectMetadata) {
"use strict";
/**
* Creates a new metadata object for a Component subclass.
*
* @param {string} sClassName fully qualified name of the class that is described by this metadata object
* @param {object} oStaticInfo static info to construct the metadata from
*
* @experimental Since 1.9.2. The Component concept is still under construction, so some implementation details can be changed in future.
* @class
* @author SAP SE
* @version 1.28.5
* @since 1.9.2
* @alias sap.ui.core.ComponentMetadata
*/
var ComponentMetadata = function(sClassName, oClassInfo) {
// call super constructor
ManagedObjectMetadata.apply(this, arguments);
};
//chain the prototypes
ComponentMetadata.prototype = jQuery.sap.newObject(ManagedObjectMetadata.prototype);
ComponentMetadata.preprocessClassInfo = function(oClassInfo) {
// if the component is a string we convert this into a "_src" metadata entry
// the specific metadata object can decide to support this or gracefully ignore it
// basically the ComponentMetadata makes use of this feature
if (oClassInfo && typeof oClassInfo.metadata === "string") {
oClassInfo.metadata = {
_src: oClassInfo.metadata
};
}
return oClassInfo;
};
ComponentMetadata.prototype.applySettings = function(oClassInfo) {
var oStaticInfo = oClassInfo.metadata;
// if the component metadata loadFromFile feature is active then
// the component metadata will be loaded from the specified file
// which needs to be located next to the Component.js file.
var sName = this.getName(),
sPackage = sName.replace(/\.\w+?$/, "");
if (oStaticInfo._src) {
if (oStaticInfo._src == "component.json") {
jQuery.sap.log.warning("Usage of declaration \"metadata: 'component.json'\" is deprecated (component " + sName + "). Use \"metadata: 'json'\" instead.");
} else if (oStaticInfo._src != "json") {
throw new Error("Invalid metadata declaration for component " + sName + ": \"" + oStaticInfo._src + "\"! Use \"metadata: 'json'\" to load metadata from component.json.");
}
var sResource = sPackage.replace(/\./g, "/") + "/component.json";
jQuery.sap.log.info("The metadata of the component " + sName + " is loaded from file " + sResource + ".");
try {
var oResponse = jQuery.sap.loadResource(sResource, {
dataType: "json"
});
jQuery.extend(oStaticInfo, oResponse);
} catch (err) {
jQuery.sap.log.error("Failed to load component metadata from \"" + sResource + "\" (component " + sName + ")! Reason: " + err);
}
}
ManagedObjectMetadata.prototype.applySettings.call(this, oClassInfo);
// keep the information about the component name (for customizing)
this._sComponentName = sPackage;
// static initialization flag & instance count
this._bInitialized = false;
this._iInstanceCount = 0;
// get the parent component
var oParent = this.getParent(),
bIsComponentBaseClass = /^sap\.ui\.core\.(UI)?Component$/.test(sName),
sParentName = bIsComponentBaseClass && oParent && oParent._sComponentName;
// extract the manifest
var oManifest = oStaticInfo["manifest"];
// if a manifest is available we switch to load the manifest for the
// metadata instead of using the component metadata section
if (oManifest) {
// set the version of the metadata
oStaticInfo.__metadataVersion = 2;
// load the manifest if defined as string
if (typeof oManifest === "string" && oManifest === "json") {
var sResource = sPackage.replace(/\./g, "/") + "/manifest.json";
jQuery.sap.log.info("The manifest of the component " + sName + " is loaded from file " + sResource + ".");
try {
// the synchronous loading would be only relevant during the
// development time - for productive usage the Component should
// provide a preload packaging which includes the manifest
// next to the Component code - so the sync request penalty
// should be ignorable for now (async implementation will
// change the complete behavior of the constructor function)
var oResponse = jQuery.sap.loadResource(sResource, {
dataType: "json"
});
oManifest = oResponse;
} catch (err) {
jQuery.sap.log.error("Failed to load component manifest from \"" + sResource + "\" (component " + sName + ")! Reason: " + err);
// in case of error the manifest is an empty object
// to behave similar like for missing component.json
oManifest = {};
}
}
} else {
// set the version of the metadata
// no manifest => metadata version 1
oStaticInfo.__metadataVersion = 1;
oManifest = {};
}
// ensure the general property name, the namespace sap.app with the id,
// the namespace sap.ui5 and eventually the extends property
oManifest["name"] = oManifest["name"] || sName;
oManifest["sap.app"] = oManifest["sap.app"] || {
"id": sPackage // use the "package" namespace instead of the classname (without ".Component")
};
oManifest["sap.ui5"] = oManifest["sap.ui5"] || {};
if (sParentName) {
oManifest["sap.ui5"]["extends"] = oManifest["sap.ui5"]["extends"] || {};
oManifest["sap.ui5"]["extends"].component = oManifest["sap.ui5"]["extends"].component || sParentName;
}
// convert the old legacy metadata and merge with the new manifest
this._convertLegacyMetadata(oStaticInfo, oManifest);
// apply the manifest to the static info and store the static info for
// later access to specific custom entries of the manifest itself
oStaticInfo["manifest"] = oManifest;
this._oStaticInfo = oStaticInfo;
};
/**
* Static initialization of components. This function will be called by the
* component and the metadata decides whether to execute the static init code
* or not. It will be called the first time a component is initialized.
* @private
*/
ComponentMetadata.prototype.init = function() {
if (!this._bInitialized) {
// first we load the dependencies of the parent
var oParent = this.getParent();
if (oParent instanceof ComponentMetadata) {
oParent.init();
}
// first the dependencies have to be loaded (other UI5 libraries)
this._loadDependencies();
// then load the custom scripts and CSS files
this._loadIncludes();
this._bInitialized = true;
}
};
/**
* Static termination of components.
*
* TODO: Right now it is unclear when this function should be called. Just to
* make sure that we do not forget this in future.
*
* @private
*/
ComponentMetadata.prototype.exit = function() {
if (this._bInitialized) {
var oParent = this.getParent();
if (oParent instanceof ComponentMetadata) {
oParent.exit();
}
// TODO: implement unload of CSS, ...
this._bInitialized = false;
}
};
/**
* Component instances need to register themselves in this method to enable
* the customizing for this component. This will only be done for the first
* instance and only if a customizing configuration is available.
* @private
*/
ComponentMetadata.prototype.onInitComponent = function() {
var oUI5Manifest = this.getManifestEntry("sap.ui5", true),
mExtensions = oUI5Manifest && oUI5Manifest["extends"] && oUI5Manifest["extends"].extensions;
if (this._iInstanceCount === 0 && !jQuery.isEmptyObject(mExtensions)) {
jQuery.sap.require("sap.ui.core.CustomizingConfiguration");
sap.ui.core.CustomizingConfiguration.activateForComponent(this._sComponentName);
}
this._iInstanceCount++;
};
/**
* Component instances need to unregister themselves in this method to disable
* the customizing for this component. This will only be done for the last
* instance and only if a customizing configuration is available.
* @private
*/
ComponentMetadata.prototype.onExitComponent = function() {
this._iInstanceCount--;
var oUI5Manifest = this.getManifestEntry("sap.ui5", true),
mExtensions = oUI5Manifest && oUI5Manifest["extends"] && oUI5Manifest["extends"].extensions;
if (this._iInstanceCount === 0 && !jQuery.isEmptyObject(mExtensions)) {
if (sap.ui.core.CustomizingConfiguration) {
sap.ui.core.CustomizingConfiguration.deactivateForComponent(this._sComponentName);
}
}
};
/**
* Returns the version of the metadata which could be 1 or 2. 1 is for legacy
* metadata whereas 2 is for the manifest.
* @return {int} metadata version (1: legacy metadata, 2: manifest)
* @protected
* @since 1.27.1
*/
ComponentMetadata.prototype.getMetadataVersion = function() {
return this._oStaticInfo.__metadataVersion;
};
/**
* Returns the manifest defined in the metadata of the component.
* If not specified, the return value is null.
* @return {Object} manifest.
* @public
* @since 1.27.1
*/
ComponentMetadata.prototype.getManifest = function() {
// only a copy of the manifest will be returned to make sure that it
// cannot be modified - TODO: think about Object.freeze() instead
return jQuery.extend(true, {}, this._oStaticInfo.manifest);
};
/**
* Returns the manifest configuration entry with the specified key (Must be a JSON object).
* If no key is specified, the return value is null.
*
* Example:
* <code>
* sap.ui.core.Component.extend("sample.Component", {
* metadata: {
* manifest: {
* "my.custom.config" : {
* "property1" : true,
* "property2" : "Something else"
* }
* }
* }
* });
* </code>
*
* The configuration above can be accessed via <code>sample.Component.getMetadata().getManifestEntry("my.custom.config")</code>.
*
* @param {string} sKey key of the custom configuration (must be prefixed with a namespace / separated with dots)
* @param {boolean} [bMerged] whether the custom configuration should be merged with components parent custom configuration.
* @return {Object} custom Component configuration with the specified key.
* @public
* @since 1.27.1
*/
ComponentMetadata.prototype.getManifestEntry = function(sKey, bMerged) {
if (!sKey || sKey.indexOf(".") <= 0) {
jQuery.sap.log.warning("Manifest entries with keys without namespace prefix can not be read via getManifestEntry. Key: " + sKey + ", Component: " + this.getName());
return null;
}
var oParent,
oManifest = this.getManifest(),
oData = oManifest && oManifest[sKey] || {};
if (!jQuery.isPlainObject(oData)) {
jQuery.sap.log.warning("Custom Manifest entry with key '" + sKey + "' must be an object. Component: " + this.getName());
return null;
}
if (bMerged && (oParent = this.getParent()) instanceof ComponentMetadata) {
return jQuery.extend(true, {}, oParent.getManifestEntry(sKey, bMerged), oData);
}
return jQuery.extend(true, {}, oData);
};
/**
* Returns the custom Component configuration entry with the specified key (Must be a JSON object).
* If no key is specified, the return value is null.
*
* Example:
* <code>
* sap.ui.core.Component.extend("sample.Component", {
* metadata: {
* "my.custom.config" : {
* "property1" : true,
* "property2" : "Something else"
* }
* }
* });
* </code>
*
* The configuration above can be accessed via <code>sample.Component.getMetadata().getCustomEntry("my.custom.config")</code>.
*
* @param {string} sKey key of the custom configuration (must be prefixed with a namespace)
* @param {boolean} bMerged whether the custom configuration should be merged with components parent custom configuration.
* @return {Object} custom Component configuration with the specified key.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifestEntry
*/
ComponentMetadata.prototype.getCustomEntry = function(sKey, bMerged) {
if (!sKey || sKey.indexOf(".") <= 0) {
jQuery.sap.log.warning("Component Metadata entries with keys without namespace prefix can not be read via getCustomEntry. Key: " + sKey + ", Component: " + this.getName());
return null;
}
var oParent,
oData = this._oStaticInfo[sKey] || {};
if (!jQuery.isPlainObject(oData)) {
jQuery.sap.log.warning("Custom Component Metadata entry with key '" + sKey + "' must be an object. Component: " + this.getName());
return null;
}
if (bMerged && (oParent = this.getParent()) instanceof ComponentMetadata) {
return jQuery.extend(true, {}, oParent.getCustomEntry(sKey, bMerged), oData);
}
return jQuery.extend(true, {}, oData);
};
/**
* Returns the name of the Component (which is the namespace only with the module name)
* @return {string} Component name
* @public
*/
ComponentMetadata.prototype.getComponentName = function() {
return this._sComponentName;
};
/**
* Returns the dependencies defined in the metadata of the component. If not specified, the return value is null.
* @return {Object} Component dependencies.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getDependencies = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getDependencies is deprecated!");
if (!this._oLegacyDependencies) {
var oUI5Manifest = this.getManifestEntry("sap.ui5"),
mDependencies = oUI5Manifest && oUI5Manifest.dependencies,
sUI5Version = mDependencies && mDependencies.minUI5Version || null,
mLibs = mDependencies && mDependencies.libs || {},
mComponents = mDependencies && mDependencies.components || {};
var mLegacyDependencies = {
ui5version: sUI5Version,
libs: [],
components: []
};
for (var sLib in mLibs) {
mLegacyDependencies.libs.push(sLib);
}
for (var sComponent in mComponents) {
mLegacyDependencies.components.push(sComponent);
}
this._oLegacyDependencies = mLegacyDependencies;
}
return this._oLegacyDependencies;
};
/**
* Returns the array of the included files that the Component requires such as css and js. If not specified or the array is empty, the return value is null.
* @return {string[]} Included files.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getIncludes = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getIncludes is deprecated!");
if (!this._aLegacyIncludes) {
var aIncludes = [],
oUI5Manifest = this.getManifestEntry("sap.ui5"),
mResources = oUI5Manifest && oUI5Manifest.resources || {},
aCSSResources = mResources && mResources.css || [],
aJSResources = mResources && mResources.js || [];
for (var i = 0, l = aCSSResources.length; i < l; i++) {
if (aCSSResources[i] && aCSSResources[i].uri) {
aIncludes.push(aCSSResources[i].uri);
}
}
for (var i = 0, l = aJSResources.length; i < l; i++) {
if (aJSResources[i] && aJSResources[i].uri) {
aIncludes.push(aJSResources[i].uri);
}
}
this._aLegacyIncludes = (aIncludes.length > 0) ? aIncludes : null;
}
return this._aLegacyIncludes;
};
/**
* Returns the required version of SAP UI5 defined in the metadata of the Component. If returned value is null, then no special UI5 version is required.
* @return {string} Required version of UI5 or if not specified then null.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getUI5Version = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getUI5Version is deprecated!");
var oUI5Manifest = this.getManifestEntry("sap.ui5");
return oUI5Manifest && oUI5Manifest.dependencies && oUI5Manifest.dependencies.minUI5Version;
};
/**
* Returns array of components specified in the metadata of the Component. If not specified or the array is empty, the return value is null.
* @return {string[]} Required Components.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getComponents = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getComponents is deprecated!");
return this.getDependencies().components;
};
/**
* Returns array of libraries specified in metadata of the Component, that are automatically loaded when an instance of the component is created.
* If not specified or the array is empty, the return value is null.
* @return {string[]} Required libraries.
* @public
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getLibs = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getLibs is deprecated!");
return this.getDependencies().libs;
};
/**
* Returns the version of the component. If not specified, the return value is null.
* @return {string} The version of the component.
* @public
*/
ComponentMetadata.prototype.getVersion = function() {
var oAppManifest = this.getManifestEntry("sap.app");
return oAppManifest && oAppManifest.applicationVersion && oAppManifest.applicationVersion.version;
};
/**
* Returns a copy of the configuration property to disallow modifications. If no
* key is specified it returns the complete configuration property.
* @param {string} [sKey] the key of the configuration property
* @param {boolean} [bDoNotMerge] true, to return only the local configuration
* @return {object} the value of the configuration property
* @public
* @since 1.15.1
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getConfig = function(sKey, bDoNotMerge) {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getConfig is deprecated!");
var oUI5Manifest = this.getManifestEntry("sap.ui5", !bDoNotMerge),
mConfig = oUI5Manifest && oUI5Manifest.config;
// return the configuration
return jQuery.extend(true, {}, mConfig && sKey ? mConfig[sKey] : mConfig);
};
/**
* Returns a copy of the customizing property
* @param {boolean} [bDoNotMerge] true, to return only the local customizing config
* @return {object} the value of the customizing property
* @private
* @since 1.15.1
* @experimental Since 1.15.1. Implementation might change.
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getCustomizing = function(bDoNotMerge) {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getCustomizing is deprecated!");
var oUI5Manifest = this.getManifestEntry("sap.ui5", !bDoNotMerge),
mExtensions = jQuery.extend(true, {}, oUI5Manifest && oUI5Manifest["extends"] && oUI5Manifest["extends"].extensions);
// return the exensions object
return mExtensions;
};
/**
* Returns the models configuration which defines the available models of the
* component.
* @param {boolean} [bDoNotMerge] true, to return only the local model config
* @return {object} models configuration
* @private
* @since 1.15.1
* @experimental Since 1.15.1. Implementation might change.
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getModels = function(bDoNotMerge) {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getModels is deprecated!");
if (!this._oLegacyModels) {
this._oLegacyModels = {};
var oUI5Manifest = this.getManifestEntry("sap.ui5"),
mDataSources = oUI5Manifest && oUI5Manifest.models || {};
for (var sDataSource in mDataSources) {
var oDataSource = mDataSources[sDataSource];
this._oLegacyModels[sDataSource] = oDataSource.settings || {};
this._oLegacyModels[sDataSource].type = oDataSource.type;
}
}
// deep copy of the legacy models object
var oParent,
mModels = jQuery.extend(true, {}, this._oLegacyModels);
// merge the models object if defined via parameter
if (!bDoNotMerge && (oParent = this.getParent()) instanceof ComponentMetadata) {
mModels = jQuery.extend(true, {}, oParent.getModels(), mModels);
}
// return a clone of the models
return mModels;
};
/**
* Returns messaging flag
*
* @return {boolean} bMessaging Messaging enabled/disabled
* @private
* @since 1.28.0
* @deprecated Since 1.28.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.handleValidation = function() {
//jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.handleValidation is deprecated!");
var oUI5Manifest = this.getManifestEntry("sap.ui5");
return oUI5Manifest && oUI5Manifest.handleValidation;
};
/**
* Returns the services configuration which defines the available services of the
* component.
* @return {object} services configuration
* @private
* @since 1.15.1
* @experimental Since 1.15.1. Implementation might change.
* @deprecated Since 1.27.1. Please use the sap.ui.core.ComponentMetadata#getManifest
*/
ComponentMetadata.prototype.getServices = function() {
jQuery.sap.log.warning("Usage of sap.ui.core.ComponentMetadata.protoype.getServices is deprecated!");
// legacy API - for the manifest services has a different meaning!
return this._oStaticInfo.services || {};
};
/**
* Loads the included CSS and JavaScript resources. The resources will be
* resoloved relative to the component location.
*
* @private
*/
ComponentMetadata.prototype._loadIncludes = function() {
var oUI5Manifest = this.getManifestEntry("sap.ui5");
var mResources = oUI5Manifest["resources"];
if (!mResources) {
return;
}
var sComponentName = this.getComponentName();
// load JS files
var aJSResources = mResources["js"];
if (aJSResources) {
for (var i = 0; i < aJSResources.length; i++) {
var oJSResource = aJSResources[i];
var sFile = oJSResource.uri;
if (sFile) {
// load javascript file
var m = sFile.match(/\.js$/i);
if (m) {
// prepend lib name to path, remove extension
var sPath = sComponentName.replace(/\./g, '/') + (sFile.slice(0, 1) === '/' ? '' : '/') + sFile.slice(0, m.index);
jQuery.sap.log.info("Component \"" + this.getName() + "\" is loading JS: \"" + sPath + "\"");
// call internal require variant that accepts a requireJS path
jQuery.sap._requirePath(sPath);
}
}
}
}
// include CSS files
var aCSSResources = mResources["css"];
if (aCSSResources) {
for (var j = 0; j < aCSSResources.length; j++) {
var oCSSResource = aCSSResources[j];
if (oCSSResource.uri) {
var sCssUrl = sap.ui.resource(sComponentName, oCSSResource.uri);
jQuery.sap.log.info("Component \"" + this.getName() + "\" is loading CSS: \"" + sCssUrl + "\"");
jQuery.sap.includeStyleSheet(sCssUrl, oCSSResource.id);
}
}
}
};
/**
* Load external dependencies (like libraries and components)
*
* @private
*/
ComponentMetadata.prototype._loadDependencies = function() {
// afterwards we load our dependencies!
var that = this,
oDep = this.getDependencies();
if (oDep) {
// load the libraries
var aLibraries = oDep.libs;
if (aLibraries) {
for (var i = 0, l = aLibraries.length; i < l; i++) {
var sLib = aLibraries[i];
jQuery.sap.log.info("Component \"" + that.getName() + "\" is loading library: \"" + sLib + "\"");
sap.ui.getCore().loadLibrary(sLib);
}
}
// load the components
var aComponents = oDep.components;
if (aComponents) {
for (var i = 0, l = aComponents.length; i < l; i++) {
var sName = aComponents[i];
jQuery.sap.log.info("Component \"" + that.getName() + "\" is loading component: \"" + sName + ".Component\"");
sap.ui.component.load({
name: sName
});
}
}
}
};
/**
* Converts the legacy metadata into the new manifest format
*
* @private
*/
ComponentMetadata.prototype._convertLegacyMetadata = function(oStaticInfo, oManifest) {
// this function can be outsourced in future when the ComponentMetadata
// is not used anymore and the new Application manifest is used -
// but for now we keep it as it will be one of the common use cases
// to have the classical ComponentMetadata and this should be
// transformed into the new manifest structure for compatibility
// converter for array with string values to object
var fnCreateObject = function(a, fnCallback) {
var o = {};
if (a) {
for (var i = 0, l = a.length; i < l; i++) {
var oValue = a[i];
if (typeof oValue === "string") {
o[oValue] = typeof fnCallback === "function" && fnCallback(oValue) || {};
}
}
}
return o;
};
// add the old information on component metadata to the manifest info
var oAppManifest = oManifest["sap.app"];
var oUI5Manifest = oManifest["sap.ui5"];
// we do not merge the manifest and the metadata - once a manifest
// entry exists, the metadata entries will be ignored and the specific
// metadata entry needs to be migrated into the manifest.
for (var sName in oStaticInfo) {
var oValue = oStaticInfo[sName];
if (oValue !== undefined) {
switch (sName) {
case "name":
oManifest[sName] = oManifest[sName] || oValue;
oAppManifest["id"] = oAppManifest["id"] || oValue;
break;
case "description":
case "keywords":
oAppManifest[sName] = oAppManifest[sName] || oValue;
break;
case "version":
var mAppVersion = oAppManifest.applicationVersion = oAppManifest.applicationVersion || {};
mAppVersion.version = mAppVersion.version || oValue;
break;
case "config":
oUI5Manifest[sName] = oUI5Manifest[sName] || oValue;
break;
case "customizing":
var mExtends = oUI5Manifest["extends"] = oUI5Manifest["extends"] || {};
mExtends.extensions = mExtends.extensions || oValue;
break;
case "dependencies":
if (!oUI5Manifest[sName]) {
oUI5Manifest[sName] = {};
oUI5Manifest[sName].minUI5Version = oValue.ui5version;
oUI5Manifest[sName].libs = fnCreateObject(oValue.libs);
oUI5Manifest[sName].components = fnCreateObject(oValue.components);
}
break;
case "includes":
if (!oUI5Manifest["resources"]) {
oUI5Manifest["resources"] = {};
if (oValue && oValue.length > 0) {
for (var i = 0, l = oValue.length; i < l; i++) {
var sResource = oValue[i];
var m = sResource.match(/\.(css|js)$/i);
if (m) {
oUI5Manifest["resources"][m[1]] = oUI5Manifest["resources"][m[1]] || [];
oUI5Manifest["resources"][m[1]].push({
"uri": sResource
});
}
}
}
}
break;
case "handleValidation":
if (oUI5Manifest[sName] === undefined) {
oUI5Manifest[sName] = oValue;
}
break;
case "models":
if (!oUI5Manifest["models"]) {
var oModels = {};
for (var sModel in oValue) {
var oDS = oValue[sModel];
var oModel = {
settings: {}
};
for (var sDSSetting in oDS) {
var oDSSetting = oDS[sDSSetting];
switch (sDSSetting) {
case "type":
oModel[sDSSetting] = oDSSetting;
break;
default:
oModel.settings[sDSSetting] = oDSSetting;
}
}
oModels[sModel] = oModel;
}
oUI5Manifest["models"] = oModels;
}
break;
// no default
}
}
}
};
return ComponentMetadata;
}, /* bExport= */ true);
},
"sap/ui/core/Configuration.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
//Provides class sap.ui.core.Configuration
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Object', './Locale', 'sap/ui/thirdparty/URI'],
function(jQuery, BaseObject, Locale, URI1) {
"use strict";
/*global URI *///declare unusual global vars for JSLint/SAPUI5 validation
/**
* Creates a new Configuration object.
*
* @class Collects and stores the configuration of the current environment.
*
* The Configuration is initialized once when the {@link sap.ui.core.Core} is created.
* There are different ways to set the environment configuration (in ascending priority):
* <ol>
* <li>System defined defaults
* <li>Server wide defaults, read from /sap-ui-config.json
* <li>Properties of the global configuration object window["sap-ui-config"]
* <li>A configuration string in the data-sap-ui-config attribute of the bootstrap tag
* <li>Individual data-sap-ui-xyz attributes of the bootstrap tag
* <li>Using URL parameters
* <li>Setters in this Configuration object (only for some parameters)
* </ol>
*
* That is, attributes of the DOM reference override the system defaults, URL parameters
* override the DOM attributes (where empty URL parameters set the parameter back to its
* system default). Calling setters at runtime will override any previous settings
* calculated during object creation.
*
* The naming convention for parameters is:
* <ul>
* <li>in the URL : sap-ui-<i>PARAMETER-NAME</i>="value"
* <li>in the DOM : data-sap-ui-<i>PARAMETER-NAME</i>="value"
* </ul>
* where <i>PARAMETER-NAME</i> is the name of the parameter in lower case.
*
* Values of boolean parameters are case insensitive where "true" and "x" are interpreted as true.
*
* @extends sap.ui.base.Object
* @author Frank Weigel (Martin Schaus)
* @constructor
* @public
* @alias sap.ui.core.Configuration
*/
var Configuration = BaseObject.extend("sap.ui.core.Configuration", /** @lends sap.ui.core.Configuration.prototype */ {
constructor : function(oCore) {
this._oCore = oCore;
function detectLanguage() {
var match;
if (!!sap.ui.Device.os.android) {
// on Android, navigator.language is hardcoded to 'en', so check UserAgent string instead
match = navigator.userAgent.match(/\s([a-z]{2}-[a-z]{2})[;)]/i);
if ( match ) {
return match[1];
}
// okay, we couldn't find a language setting. It might be better to fallback to 'en' instead of having no language
}
return (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage || navigator.browserLanguage;
}
// definition of supported settings
var M_SETTINGS = {
"theme" : { type : "string", defaultValue : "base" },
"language" : { type : "string", defaultValue : detectLanguage() },
"formatLocale" : { type : "string", defaultValue : null },
// "timezone" : "UTC",
"accessibility" : { type : "boolean", defaultValue : true },
"autoAriaBodyRole" : { type : "boolean", defaultValue : true, noUrl:true }, //whether the framework automatically adds automatically the ARIA role 'application' to the html body
"animation" : { type : "boolean", defaultValue : true },
"rtl" : { type : "boolean", defaultValue : null },
"debug" : { type : "boolean", defaultValue : false },
"inspect" : { type : "boolean", defaultValue : false },
"originInfo" : { type : "boolean", defaultValue : false },
"noConflict" : { type : "boolean", defaultValue : false, noUrl:true },
"noDuplicateIds" : { type : "boolean", defaultValue : true },
"trace" : { type : "boolean", defaultValue : false, noUrl:true },
"modules" : { type : "string[]", defaultValue : [], noUrl:true },
"areas" : { type : "string[]", defaultValue : null, noUrl:true },
// "libs" : { type : "string[]", defaultValue : [], noUrl:true }, deprecated, handled below
"onInit" : { type : "code", defaultValue : undefined, noUrl:true },
"uidPrefix" : { type : "string", defaultValue : "__", noUrl:true },
"ignoreUrlParams" : { type : "boolean", defaultValue : false, noUrl:true },
"weinreServer" : { type : "string", defaultValue : "", noUrl:true },
"weinreId" : { type : "string", defaultValue : "" },
"preload" : { type : "string", defaultValue : "auto" },
"rootComponent" : { type : "string", defaultValue : "", noUrl:true },
"preloadLibCss" : { type : "string[]", defaultValue : [] },
"application" : { type : "string", defaultValue : "" },
"appCacheBuster" : { type : "string[]", defaultValue : [] },
"bindingSyntax" : { type : "string", defaultValue : "default", noUrl:true }, // default|simple|complex
"whitelistService" : { type : "string", defaultValue : null, noUrl: true }, // url/to/service
"frameOptions" : { type : "string", defaultValue : "allow", noUrl: true }, // allow/deny/trusted
"frameOptionsConfig" : { type : "object", defaultValue : undefined, noUrl:true }, // advanced frame options configuration
"xx-rootComponentNode" : { type : "string", defaultValue : "", noUrl:true },
"xx-appCacheBusterMode" : { type : "string", defaultValue : "sync" },
"xx-appCacheBusterHooks" : { type : "object", defaultValue : undefined, noUrl:true }, // e.g.: { handleURL: fn, onIndexLoad: fn, onIndexLoaded: fn }
"xx-disableCustomizing" : { type : "boolean", defaultValue : false, noUrl:true },
"xx-loadAllMode" : { type : "boolean", defaultValue : false, noUrl:true },
"xx-test-mobile" : { type : "boolean", defaultValue : false },
"xx-componentPreload" : { type : "string", defaultValue : "" },
"xx-designMode" : { type : "boolean", defaultValue : false },
"xx-supportedLanguages" : { type : "string[]", defaultValue : [] }, // *=any, sapui5 or list of locales
"xx-bootTask" : { type : "function", defaultValue : undefined, noUrl:true },
"xx-suppressDeactivationOfControllerCode" : { type : "boolean", defaultValue : false }, //temporarily to suppress the deactivation of controller code in design mode
"xx-lesssupport" : { type : "boolean", defaultValue : false },
"xx-handleValidation" : { type : "boolean", defaultValue : false },
"statistics" : { type : "boolean", defaultValue : false }
};
var M_COMPAT_FEATURES = {
"xx-test" : "1.15", //for testing purposes only
"flexBoxPolyfill" : "1.14",
"sapMeTabContainer" : "1.14",
"sapMeProgessIndicator" : "1.14",
"sapMGrowingList" : "1.14",
"sapMListAsTable" : "1.14",
"sapMDialogWithPadding" : "1.14",
"sapCoreBindingSyntax" : "1.24"
};
this.oFormatSettings = new Configuration.FormatSettings(this);
/* Object that carries the real configuration data */
/*eslint-disable consistent-this */
var config = this;
/*eslint-enable consistent-this */
function setValue(sName, sValue) {
if ( typeof sValue === "undefined" || sValue === null ) {
return;
}
switch (M_SETTINGS[sName].type) {
case "boolean":
if ( typeof sValue === "string" ) {
if (M_SETTINGS[sName].defaultValue) {
config[sName] = sValue.toLowerCase() != "false";
} else {
config[sName] = sValue.toLowerCase() === "true" || sValue.toLowerCase() === "x";
}
} else {
// boolean etc.
config[sName] = !!sValue;
}
break;
case "string":
config[sName] = "" + sValue; // enforce string
break;
case "code":
config[sName] = typeof sValue === "function" ? sValue : String(sValue);
break;
case "function":
if ( typeof sValue !== "function" ) {
throw new Error("unsupported value");
}
config[sName] = sValue;
break;
case "string[]":
if ( jQuery.isArray(sValue) ) {
config[sName] = sValue;
} else if ( typeof sValue === "string" ) {
config[sName] = jQuery.map(sValue.split(/[ ,;]/), function($) {
return jQuery.trim($);
});
} else {
throw new Error("unsupported value");
}
break;
case "object":
if ( typeof sValue !== "object" ) {
throw new Error("unsupported value");
}
config[sName] = sValue;
break;
default:
throw new Error("illegal state");
}
}
function validateThemeRoot(sThemeRoot) {
var oThemeRoot,
sPath;
try {
oThemeRoot = new URI(sThemeRoot, window.location.href).normalize();
sPath = oThemeRoot.path();
return sPath + (sPath.slice(-1) === '/' ? '' : '/') + "UI5/";
} catch (e) {
// malformed URL are also not accepted
}
}
// 1. collect the defaults
for (var n in M_SETTINGS ) {
config[n] = M_SETTINGS[n].defaultValue;
}
// 2. read server wide sapui5 configuration
/* TODO: RETHINK server wide sapui5 configuration to make it optional
currently it is forcing a request which is annoying customers :
- Think about an option which enables loading of server wide config!
var sUrl = "/sap-ui-config.json";
var oResponse = jQuery.sap.sjax({url:sUrl});
if (oResponse.success) {
var oServerCfg = {};
if (typeof oResponse.data == "string") {
try {
oServerCfg = jQuery.parseJSON(oResponse.data);
} catch(ex) {
jQuery.sap.log.warning("Error when parsing the JSON configuration content from " + sUrl + " : " + ex);
}
} else {
oServerCfg = oResponse.data;
}
for (var n in M_SETTINGS) {
if (oServerCfg[n]) {
setValue(n, oServerCfg[n]);
}
}
}
*/
// 3.-5. apply settings from global config object (already merged with script tag attributes)
var oCfg = window["sap-ui-config"] || {};
oCfg.oninit = oCfg.oninit || oCfg["evt-oninit"];
for (var n in M_SETTINGS) {
if ( oCfg.hasOwnProperty(n.toLowerCase()) ) {
setValue(n, oCfg[n.toLowerCase()]);
} else if ( !/^xx-/.test(n) && oCfg.hasOwnProperty("xx-" + n.toLowerCase()) ) {
setValue(n, oCfg["xx-" + n.toLowerCase()]);
}
}
// if libs are configured, convert them to modules and prepend them to the existing modules list
if ( oCfg.libs ) {
config.modules = jQuery.map(oCfg.libs.split(","), function($) {
return jQuery.trim($) + ".library";
}).concat(config.modules);
}
var PARAM_CVERS = "compatversion";
var DEFAULT_CVERS = oCfg[PARAM_CVERS];
var BASE_CVERS = jQuery.sap.Version("1.14");
this._compatversion = {};
function _getCVers(key){
var v = !key ? DEFAULT_CVERS || BASE_CVERS.toString()
: oCfg[PARAM_CVERS + "-" + key.toLowerCase()] || DEFAULT_CVERS || M_COMPAT_FEATURES[key] || BASE_CVERS.toString();
v = jQuery.sap.Version(v.toLowerCase() === "edge" ? sap.ui.version : v);
//Only major and minor version are relevant
return jQuery.sap.Version(v.getMajor(), v.getMinor());
}
this._compatversion._default = _getCVers();
for (var n in M_COMPAT_FEATURES) {
this._compatversion[n] = _getCVers(n);
}
// 6. apply the settings from the url (only if not blocked by app configuration)
if ( !config.ignoreUrlParams ) {
var sUrlPrefix = "sap-ui-";
var oUriParams = jQuery.sap.getUriParameters();
// map SAP parameters (if later as sap-ui parameter set this wins)
if (oUriParams.mParams['sap-locale'] || oUriParams.mParams['sap-language']) {
// map sap-locale or sap-language to sap-ui-language
// if sap-language is used, handle some legacy language codes
var sValue = oUriParams.get('sap-locale') || M_ABAP_LANGUAGE_TO_LOCALE[oUriParams.get('sap-language').toUpperCase()] || oUriParams.get('sap-language');
if (sValue === "") {
//empty URL parameters set the parameter back to its system default
config['language'] = M_SETTINGS['language'].defaultValue;
} else {
//sets the value (null or empty value ignored)
setValue('language', sValue);
}
}
if (oUriParams.mParams['sap-rtl']) {
// "" = false, "X", "x" = true
var sValue = oUriParams.get('sap-rtl');
if (sValue === "X" || sValue === "x") {
setValue('rtl', true);
} else {
setValue('rtl', false);
}
}
if (oUriParams.mParams['sap-theme']) {
var sValue = oUriParams.get('sap-theme');
if (sValue === "") {
// empty URL parameters set the parameter back to its system default
config['theme'] = M_SETTINGS['theme'].defaultValue;
} else {
setValue('theme', sValue);
}
}
if (oUriParams.mParams['sap-statistics']) {
var sValue = oUriParams.get('sap-statistics');
setValue('statistics', sValue);
}
// now analyze sap-ui parameters
for (var n in M_SETTINGS) {
if ( M_SETTINGS[n].noUrl ) {
continue;
}
var sValue = oUriParams.get(sUrlPrefix + n);
if ( sValue == null && !/^xx-/.test(n) ) {
sValue = oUriParams.get(sUrlPrefix + "xx-" + n);
}
if (sValue === "") {
//empty URL parameters set the parameter back to its system default
config[n] = M_SETTINGS[n].defaultValue;
} else {
//sets the value (null or empty value ignored)
setValue(n, sValue);
}
}
}
// calculate RTL mode
this.derivedRTL = Locale._impliesRTL(config.language);
// analyze theme parameter
var sTheme = config.theme;
var sThemeRoot;
var iIndex = sTheme.indexOf("@");
if (iIndex >= 0) {
sThemeRoot = validateThemeRoot(sTheme.slice(iIndex + 1));
if ( sThemeRoot ) {
config.theme = sTheme.slice(0, iIndex);
config.themeRoot = sThemeRoot;
} else {
// fallback to non-URL parameter (if not equal to sTheme)
config.theme = (oCfg.theme && oCfg.theme !== sTheme) ? oCfg.theme : "base";
iIndex = -1; // enable theme mapping below
}
}
config.theme = this._normalizeTheme(config.theme, sThemeRoot);
var aCoreLangs = config['languagesDeliveredWithCore'] = Locale._coreI18nLocales;
var aLangs = config['xx-supportedLanguages'];
if ( aLangs.length === 0 || (aLangs.length === 1 && aLangs[0] === '*') ) {
aLangs = [];
} else if ( aLangs.length === 1 && aLangs[0] === 'default' ) {
aLangs = aCoreLangs || [];
}
config['xx-supportedLanguages'] = aLangs;
// determine default for binding syntax
if ( config["bindingSyntax"] === "default" ) {
config["bindingSyntax"] = (config.getCompatibilityVersion("sapCoreBindingSyntax").compareTo("1.26") < 0) ? "simple" : "complex";
}
if (!config["frameOptions"] ||
(config["frameOptions"] !== 'allow'
&& config["frameOptions"] !== 'deny'
&& config["frameOptions"] !== 'trusted')) {
// default
config["frameOptions"] = 'allow';
}
// log all non default value
for (var n in M_SETTINGS) {
if ( config[n] !== M_SETTINGS[n].defaultValue ) {
jQuery.sap.log.info(" " + n + " = " + config[n]);
}
}
},
/**
* Returns the version of the framework.
*
* Similar to <code>sap.ui.version</code>.
*
* @return {jQuery.sap.Version} the version
* @public
*/
getVersion : function () {
if (this._version) {
return this._version;
}
this._version = new jQuery.sap.Version(sap.ui.version);
return this._version;
},
/**
* Returns the used compatibility version for the given feature.
*
* @param {string} sFeature the key of desired feature
* @return {jQuery.sap.Version} the used compatibility version
* @public
*/
getCompatibilityVersion : function (sFeature) {
if (typeof (sFeature) === "string" && this._compatversion[sFeature]) {
return this._compatversion[sFeature];
}
return this._compatversion._default;
},
/**
* Returns the theme name
* @return {string} the theme name
* @public
*/
getTheme : function () {
return this.theme;
},
/**
* Allows setting the theme name
* @param {string} sTheme the theme name
* @return {sap.ui.core.Configuration} <code>this</code> to allow method chaining
* @private
*/
_setTheme : function (sTheme) {
this.theme = sTheme;
return this;
},
/**
* Normalize the given theme, resolve known aliases
* @private
*/
_normalizeTheme : function (sTheme, sThemeBaseUrl) {
if ( sTheme && sThemeBaseUrl == null && sTheme.match(/^sap_corbu$/i) ) {
return "sap_goldreflection";
}
return sTheme;
},
/**
* Returns the language string with language and country code
* @return {string} the language string with language and country code
* @public
*/
getLanguage : function () {
return this.language;
},
/**
* Sets a new language tag to be used from now on for language/region dependent
* functionality (e.g. formatting, data types, translated texts, ...).
*
* When the language has changed, the Core will fire its
* {@link sap.ui.core.Core#event:localizationChanged localizationChanged} event.
*
* The framework <strong>does not</strong> guarantee that already created, language
* dependent objects will be updated by this call. It therefore remains best practice
* for applications to switch the language early, e.g. before any language dependent
* objects are created. Applications that need to support more dynamic changes of
* the language should listen to the <code>localizationChanged</code> event and adapt
* all language dependent objects that they use (e.g. by rebuilding their UI).
*
* Currently, the framework notifies the following objects about a change of the
* localization settings before it fires the <code>localizationChanged</code> event:
*
* <ul>
* <li>date and number data types that are used in property bindings or composite
* bindings in existing Elements, Controls, UIAreas or Components</li>
* <li>ResourceModels currently assigned to the Core, an UIArea, Component,
* Element or Control</li>
* <li>Elements or Controls that implement the <code>onLocalizationChanged</code> hook.
* </ul>
*
* It furthermore derives the RTL mode from the new language, if no explicit RTL
* mode has been set. If the RTL mode changes, the following additional actions will be taken:
*
* <ul>
* <li>the URLs of already loaded library theme files will be changed</li>
* <li>the <code>dir</code> attribute of the page will be changed to reflect the new mode.</li>
* <li>all UIAreas will be invalidated (which results in a rendering of the whole UI5 UI)</li>
* </ul>
*
* @param {string} sLanguage the new language as a BCP47 compliant language tag; case doesn't matter
* and underscores can be used instead of a dashes to separate components (compatibility with Java Locale Ids)
* @return {sap.ui.core.Configuration} <code>this</code> to allow method chaining
*
* @experimental Since 1.11.1 - See method documentation for restrictions.
* @public
*/
setLanguage : function (sLanguage) {
check(typeof sLanguage === "string" && sLanguage, "sLanguage must be a BCP47 language tag or Java Locale id or null"); // TODO delegate to Locale?
var bOldRTL = this.getRTL(),
mChanges;
if ( sLanguage != this.language ) {
mChanges = this._collect();
this.language = mChanges.language = sLanguage;
this.derivedRTL = Locale._impliesRTL(sLanguage);
if ( bOldRTL != this.getRTL() ) {
mChanges.rtl = this.getRTL();
}
this._endCollect();
}
return this;
},
/**
* Returns the active locale for the current session.
* The locale is derived from the {@link #getLanguage language} property.
* @return {sap.ui.core.Locale} the locale
* @public
*/
getLocale : function () {
return new Locale(this.language);
},
/**
* Returns the format locale string with language and region code. Falls back to
* language configuration, in case it has not been explicitly defined.
*
* @return {string} the format locale string with language and country code
* @public
*/
getFormatLocale : function () {
return this.formatLocale || this.language;
},
/**
* Sets a new formatLocale to be used from now on for retrieving locale
* specific formatters. Modifying this setting does not have an impact on
* the retrieval of translated texts!
*
* Can either be set to a concrete value (a BCP-47 or Java locale compliant
* language tag) or to <code>null</code>. When set to <code>null</code> (default
* value) then locale specific formatters are retrieved for the current language.
*
* After changing the formatLocale, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link #setLanguage} for
* details and restrictions.
*
* @param {string|null} sFormatLocale the new format locale as a BCP47 compliant language tag;
* case doesn't matter and underscores can be used instead of a dashes to separate
* components (compatibility with Java Locale Ids)
* @return {sap.ui.core.Configuration} <code>this</code> to allow method chaining
*
* @experimental Since 1.11.1 - See documentation of {@link #setLanguage} for restrictions.
*/
setFormatLocale : function(sFormatLocale) {
check(sFormatLocale === null || typeof sFormatLocale === "string" && sFormatLocale, "sFormatLocale must be a BCP47 language tag or Java Locale id or null");
var mChanges;
if ( sFormatLocale != this.formatLocale ) {
mChanges = this._collect();
this.formatLocale = mChanges.formatLocale = sFormatLocale;
this._endCollect();
}
return this;
},
/**
* List of languages that the SAPUI5 core deliveres.
*
* Might return undefined if the information is not available.
*
* @experimental
*/
getLanguagesDeliveredWithCore : function() {
return this["languagesDeliveredWithCore"];
},
/**
* @experimental
*/
getSupportedLanguages : function() {
return this["xx-supportedLanguages"];
},
/**
* Returns whether the accessibility mode is used or not
* @return {boolean} whether the accessibility mode is used or not
* @public
*/
getAccessibility : function () {
return this.accessibility;
},
/**
* Returns whether the framework automatically adds automatically
* the ARIA role 'application' to the html body or not.
* @return {boolean}
* @since 1.27.0
* @public
*/
getAutoAriaBodyRole : function () {
return this.autoAriaBodyRole;
},
/**
* Returns whether the animations are globally used
* @return {boolean} whether the animations are globally used
* @public
*/
getAnimation : function () {
return this.animation;
},
/**
* Returns whether the page uses the RTL text direction.
*
* If no mode has been explicitly set (neither true nor false),
* the mode is derived from the current language setting.
*
* @return {boolean} whether the page uses the RTL text direction
* @public
*/
getRTL : function () {
// if rtl has not been set (still null), return the rtl mode derived from the language
return this.rtl === null ? this.derivedRTL : this.rtl;
},
/**
* Sets the character orientation mode to be used from now on.
*
* Can either be set to a concrete value (true meaning right-to-left,
* false meaning left-to-right) or to <code>null</code> which means that
* the character orientation mode should be derived from the current
* language (incl. region) setting.
*
* After changing the character orientation mode, the framework tries
* to update localization specific parts of the UI. See the documentation of
* {@link #setLanguage} for details and restrictions.
*
* @param {boolean|null} bRTL new character orientation mode or <code>null</code>
* @return {sap.ui.core.Configuration} <code>this</code> to allow method chaining
*
* @experimental Since 1.11.1 - See documentation of {@link #setLanguage} for restrictions.
*/
setRTL : function(bRTL) {
check(bRTL === null || typeof bRTL === "boolean", "bRTL must be null or a boolean");
var mChanges;
if ( bRTL != this.rtl ) {
mChanges = this._collect();
this.rtl = mChanges.rtl = this.getRTL();
this._endCollect();
}
return this;
},
/**
* Returns whether the page runs in debug mode
* @return {boolean} whether the page runs in debug mode
* @public
*/
getDebug : function () {
return this.debug;
},
/**
* Returns whether the UI5 control inspector is displayed
* Has only an effect when the sap-ui-debug module has been loaded
* @return {boolean} whether the UI5 control inspector is displayed
* @public
*/
getInspect : function () {
return this.inspect;
},
/**
* Returns whether the text origin information is collected
* @return {boolean} whether the text info is collected
* @public
*/
getOriginInfo : function () {
return this.originInfo;
},
/**
* Returns whether there should be an exception on any duplicate element IDs
* @return {boolean} whether there should be an exception on any duplicate element IDs
* @public
*/
getNoDuplicateIds : function () {
return this.noDuplicateIds;
},
/**
* Whether a trace view should be shown or not.
* Has only an effect when the sap-ui-debug module has been loaded
* either by explicitly loading it or by setting the 'debug' option to true.
* @return {boolean} whether a trace view should be shown
*/
getTrace : function () {
return this.trace;
},
/**
* Prefix to be used for automatically generated control IDs.
* Default is a double underscore "__".
*
* @returns {string} the prefix to be used
* @public
*/
getUIDPrefix : function() {
return this.uidPrefix;
},
/**
* Return whether the design mode is active or not.
*
* @returns {boolean} whether the design mode is active or not.
* @since 1.13.2
* @experimental Since 1.13.2
* @public
*/
getDesignMode : function() {
return this["xx-designMode"];
},
/**
* Return whether the activation of the controller code is suppressed
*
* @returns {boolean} whether the activation of the controller code is suppressed or not
* @since 1.13.2
* @experimental Since 1.13.2
* @public
*/
getSuppressDeactivationOfControllerCode : function() {
return this["xx-suppressDeactivationOfControllerCode"];
},
/**
* Return whether the controller code is deactivated. During design mode the
*
* @returns {boolean} whether the activation of the controller code is suppressed or not
* @since 1.26.4
* @experimental Since 1.26.4
* @public
*/
getControllerCodeDeactivated : function() {
return this.getDesignMode() && !this.getSuppressDeactivationOfControllerCode();
},
/**
* WEINRE server URL
*
* @returns {string} the base URL of the WEINRE server
* @public
*/
getWeinreServer : function() {
var sWeinreServer = this.weinreServer;
if (!sWeinreServer) {
// if not weinre server is configured - we expect that the weinre server
// is installed on the same machine with port no of the app + 1
sWeinreServer = window.location.protocol + "//" + window.location.hostname + ":";
sWeinreServer += (parseInt(window.location.port, 10) || 8080) + 1;
}
return sWeinreServer;
},
/**
* WEINRE session ID
*
* @returns {string} the ID to use for the WEINRE server
* @public
*/
getWeinreId : function() {
return this.weinreId;
},
/**
* The name of the application to start or empty
*
* @returns {string} name of the application
* @public
* @experimental Since 1.13.2
* @deprecated Since 1.15.1. Please use the rootComponent configuration option {@link sap.ui.core.Configuration#getRootComponent}.
*/
getApplication : function() {
return this.application;
},
/**
* The name of the root component to start or empty
*
* @returns {string} name of the root component
* @public
* @experimental Since 1.15.1
*/
getRootComponent : function() {
return this.rootComponent;
},
/**
* Base URLs to AppCacheBuster Etag-Index files
*
* @returns {string[]} array of base URLs
* @public
*/
getAppCacheBuster : function() {
return this.appCacheBuster;
},
/**
* The loading mode (sync|async|batch) of the AppCacheBuster (sync is default)
*
* @returns {string} sync | async
* @public
* @experimental Since 1.12.5
*/
getAppCacheBusterMode : function() {
return this["xx-appCacheBusterMode"];
},
/**
* Object defining the callback hooks for the AppCacheBuster like e.g.
* <code>handleURL</code>, <code>onIndexLoad</code> or <code>onIndexLoaded</code>.
*
* @returns {object} object containing the callback functions for the AppCacheBuster
* @public
* @experimental Since 1.27.0
*/
getAppCacheBusterHooks : function() {
return this["xx-appCacheBusterHooks"];
},
/**
* Flag, whether the customizing is disabled or not
*
* @returns {boolean} true if customizing is disabled
* @private
* @experimental Since 1.15.1
*/
getDisableCustomizing : function() {
return this["xx-disableCustomizing"];
},
/**
* Currently active preload mode for libraries or falsy value
*
* @returns {string} preload mode
* @private
* @since 1.16.3
*/
getPreload : function() {
return this.preload;
},
/**
* Currently active preload mode for components or falsy value
*
* @returns {string} component preload mode
* @private
* @experimental Since 1.16.3, might change completely.
*/
getComponentPreload : function() {
return this['xx-componentPreload'] || this.preload;
},
/**
* Returns a configuration object that bundles the format settings of UI5.
*
* @return {sap.ui.core.Configuration.FormatSettings} A FormatSettings object.
* @public
*/
getFormatSettings : function() {
return this.oFormatSettings;
},
_collect : function() {
var mChanges = this.mChanges || (this.mChanges = { __count : 0});
mChanges.__count++;
return mChanges;
},
_endCollect : function() {
var mChanges = this.mChanges;
if ( mChanges && (--mChanges.__count) === 0 ) {
delete mChanges.__count;
this._oCore && this._oCore.fireLocalizationChanged(mChanges);
delete this.mChanges;
}
},
/**
* Flag if statistics are requested
*
* Flag set by TechnicalInfo Popup will also be checked
* So its active if set by ULP parameter or by TechnicalInfo property
*
* @returns {boolean} statistics flag
* @private
* @since 1.20.0
*/
getStatistics : function() {
return this.statistics || window.localStorage.getItem("sap-ui-statistics") == "X";
},
/**
* Return whether native scrolling should be suppressed on touch devices
*
* @returns {boolean} whether native scrolling is suppressed on touch devices
* @since 1.20.0
* @deprecated since 1.26.0. Always use native scrolling
* @private
*/
getNoNativeScroll : function() {
return false;
},
/**
* Return whether type validation is handled by core
*
* @returns {boolean} whether whether type validation is handled by core
* @since 1.28.0
* @private
*/
getHandleValidation : function() {
return this["xx-handleValidation"];
}
});
var M_ABAP_LANGUAGE_TO_LOCALE = {
"ZH" : "zh-Hans",
"ZF" : "zh-Hant",
"1Q" : "en-US-x-saptrc",
"2Q" : "en-US-x-sappsd"
};
var M_ABAP_DATE_FORMAT_PATTERN = {
"" : {pattern: null},
"1": {pattern: "dd.MM.yyyy"},
"2": {pattern: "MM/dd/yyyy"},
"3": {pattern: "MM-dd-yyyy"},
"4": {pattern: "yyyy.MM.dd"},
"5": {pattern: "yyyy/MM/dd"},
"6": {pattern: "yyyy-MM-dd"},
"7": {pattern: "Gyy.MM.dd", ignore:true},
"8": {pattern: "Gyy/MM/dd", ignore:true},
"9": {pattern: "Gyy-MM-dd", ignore:true},
"A": {pattern: "yyyy/MM/dd", ignore:true},
"B": {pattern: "yyyy/MM/dd", ignore:true},
"C": {pattern: "yyyy/MM/dd", ignore:true}
};
var M_ABAP_TIME_FORMAT_PATTERN = {
"" : {"short": null, medium: null, dayPeriods: null},
"0": {"short": "HH:mm", medium: "HH:mm:ss", dayPeriods: null},
"1": {"short": "hh:mm a", medium: "hh:mm:ss a", dayPeriods: ["AM", "PM"]},
"2": {"short": "hh:mm a", medium: "hh:mm:ss a", dayPeriods: ["am", "pm"]},
"3": {"short": "KK:mm a", medium: "KK:mm:ss a", dayPeriods: ["AM", "PM"]},
"4": {"short": "KK:mm a", medium: "KK:mm:ss a", dayPeriods: ["am", "pm"]}
};
var M_ABAP_NUMBER_FORMAT_SYMBOLS = {
"" : {groupingSeparator: null, decimalSeparator: null},
" ": {groupingSeparator: ".", decimalSeparator: ","},
"X": {groupingSeparator: ",", decimalSeparator: "."},
"Y": {groupingSeparator: " ", decimalSeparator: ","}
};
function check(bCondition, sMessage) {
if ( !bCondition ) {
throw new Error(sMessage);
}
}
/**
* @class Encapsulates configuration settings that are related to data formatting/parsing.
*
* <b>Note:</b> When format configuration settings are modified through this class,
* UI5 only ensures that formatter objects created after that point in time will honor
* the modifications. To be on the safe side, applications should do any modifications
* early in their lifecycle or recreate any model/UI that is locale dependent.
*
* @name sap.ui.core.Configuration.FormatSettings
* @extends sap.ui.base.Object
* @public
*/
BaseObject.extend("sap.ui.core.Configuration.FormatSettings", /** @lends sap.ui.core.Configuration.FormatSettings.prototype */ {
constructor : function(oConfiguration) {
this.oConfiguration = oConfiguration;
this.mSettings = {};
this.sLegacyDateFormat = undefined;
this.sLegacyTimeFormat = undefined;
this.sLegacyNumberFormatSymbolSet = undefined;
},
/**
* Returns the locale to be used for formatting.
*
* If no such locale has been defined, this method falls back to the language,
* see {@link sap.ui.core.Configuration#getLanguage Configuration.getLanguage()}.
*
* If any user preferences for date, time or number formatting have been set,
* and if no format locale has been specified, then a special private use subtag
* is added to the locale, indicating to the framework that these user preferences
* should be applied.
*
* @return {sap.ui.core.Locale} the format locale
* @public
*/
getFormatLocale : function() {
function fallback(that) {
var l = that.oConfiguration.language;
// if any user settings have been defined, add the private use subtag "sapufmt"
if ( !jQuery.isEmptyObject(that.mSettings) ) {
// TODO move to Locale/LocaleData
if ( l.indexOf("-x-") < 0 ) {
l = l + "-x-sapufmt";
} else if ( l.indexOf("-sapufmt") <= l.indexOf("-x-") ) {
l = l + "-sapufmt";
}
}
return l;
}
return new Locale(this.oConfiguration.formatLocale || fallback(this));
},
_set : function(sKey, oValue) {
var oOldValue = this.mSettings[sKey];
if ( oValue != null ) {
this.mSettings[sKey] = oValue;
} else {
delete this.mSettings[sKey];
}
if ( (oOldValue == null != oValue == null) || !jQuery.sap.equal(oOldValue, oValue) ) {
var mChanges = this.oConfiguration._collect();
mChanges[sKey] = oValue;
this.oConfiguration._endCollect();
}
},
/**
* Returns the currently set date pattern or undefined if no pattern has been defined.
* @public
*/
getDatePattern : function(sStyle) {
jQuery.sap.assert(sStyle == "short" || sStyle == "medium" || sStyle == "long" || sStyle == "full", "sStyle must be short, medium, long or full");
return this.mSettings["dateFormat-" + sStyle];
},
/**
* Defines the preferred format pattern for the given date format style.
* Calling this method with a null or undefined pattern removes a previously set pattern.
*
* If a pattern is defined, it will be preferred over patterns derived from the current locale.
*
* See class {@link sap.ui.core.format.DateFormat} for details about the pattern syntax.
*
* After changing the date pattern, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {string} sStyle must be one of short, medium, long or full.
* @param {string} sPattern the format pattern to be used in LDML syntax.
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setDatePattern : function(sStyle, sPattern) {
check(sStyle == "short" || sStyle == "medium" || sStyle == "long" || sStyle == "full", "sStyle must be short, medium, long or full");
this._set("dateFormat-" + sStyle, sPattern);
return this;
},
/**
* Returns the currently set time pattern or undefined if no pattern has been defined.
* @public
*/
getTimePattern : function(sStyle) {
jQuery.sap.assert(sStyle == "short" || sStyle == "medium" || sStyle == "long" || sStyle == "full", "sStyle must be short, medium, long or full");
return this.mSettings["timeFormat-" + sStyle];
},
/**
* Defines the preferred format pattern for the given time format style.
* Calling this method with a null or undefined pattern removes a previously set pattern.
*
* If a pattern is defined, it will be preferred over patterns derived from the current locale.
*
* See class {@link sap.ui.core.format.DateFormat} for details about the pattern syntax.
*
* After changing the time pattern, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {string} sStyle must be one of short, medium, long or full.
* @param {string} sPattern the format pattern to be used in LDML syntax.
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setTimePattern : function(sStyle, sPattern) {
check(sStyle == "short" || sStyle == "medium" || sStyle == "long" || sStyle == "full", "sStyle must be short, medium, long or full");
this._set("timeFormat-" + sStyle, sPattern);
return this;
},
/**
* Returns the currently set number symbol of the given type or undefined if no symbol has been defined.
* @public
*/
getNumberSymbol : function(sType) {
jQuery.sap.assert(sType == "decimal" || sType == "group" || sType == "plusSign" || sType == "minusSign", "sType must be decimal, group, plusSign or minusSign");
return this.mSettings["symbols-latn-" + sType];
},
/**
* Defines the string to be used for the given number symbol.
* Calling this method with a null or undefined symbol removes a previously set symbol string.
* Note that an empty string is explicitly allowed.
*
* If a symbol is defined, it will be preferred over symbols derived from the current locale.
*
* See class {@link sap.ui.core.format.NumberFormat} for details about the symbols.
*
* After changing the number symbol, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {string} sStyle must be one of decimal, group, plusSign, minusSign.
* @param {string} sSymbol will be used to represent the given symbol type
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setNumberSymbol : function(sType, sSymbol) {
check(sType == "decimal" || sType == "group" || sType == "plusSign" || sType == "minusSign", "sType must be decimal, group, plusSign or minusSign");
this._set("symbols-latn-" + sType, sSymbol);
return this;
},
/**
* Defines the day used as the first day of the week.
* The day is set as an integer value between 0 (Sunday) and 6 (Saturday).
* Calling this method with a null or undefined symbol removes a previously set value.
*
* If a value is defined, it will be preferred over values derived from the current locale.
*
* Usually in the US the week starts on Sunday while in most European countries on Monday.
* There are special cases where you want to have the first day of week set independent of the
* user locale.
*
* After changing the first day of week, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {number} iValue must be an integer value between 0 and 6
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setFirstDayOfWeek : function(iValue) {
check(typeof iValue == "number" && iValue >= 0 && iValue <= 6, "iValue must be an integer value between 0 and 6");
this._set("weekData-firstDay", iValue);
return this;
},
_setDayPeriods : function(sWidth, aTexts) {
jQuery.sap.assert(sWidth == "narrow" || sWidth == "abbreviated" || sWidth == "wide", "sWidth must be narrow, abbreviated or wide");
this._set("dayPeriods-format-" + sWidth, aTexts);
return this;
},
/**
* Returns the currently set legacy ABAP date format (its id) or undefined if none has been set.
*
* @public
*/
getLegacyDateFormat : function() {
return this.sLegacyDateFormat || undefined;
},
/**
* Allows to specify one of the legacy ABAP date formats.
*
* This method modifies the date patterns for 'short' and 'medium' style with the corresponding ABAP
* format. When called with a null or undefined format id, any previously applied format will be removed.
*
* After changing the legacy date format, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* Note: those date formats that are not based on the Gregorian calendar (Japanese date formats '7', '8' and '9',
* Islamic date formats 'A' and 'B' and Iranian date format 'C') are not yet supported by UI5. They are accepted
* by this method for convenience (user settings from ABAP system can be used without filtering), but they are
* ignored. Instead, the formats from the current format locale will be used and a warning will be logged.
*
* @param {string} sFormatId id of the ABAP data format (one of '1','2','3','4','5','6','7','8','9','A','B','C')
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setLegacyDateFormat : function(sFormatId) {
sFormatId = sFormatId ? String(sFormatId).toUpperCase() : "";
check(!sFormatId || M_ABAP_DATE_FORMAT_PATTERN.hasOwnProperty(sFormatId), "sFormatId must be one of ['1','2','3','4','5','6','7','8','9','A','B','C'] or empty");
if ( M_ABAP_DATE_FORMAT_PATTERN[sFormatId].ignore ) {
jQuery.sap.log.warning("The ABAP date format '" + sFormatId + "' (" + M_ABAP_DATE_FORMAT_PATTERN[sFormatId].pattern + ") is not supported yet. Falling back to locale specific date formats.");
sFormatId = "";
}
var mChanges = this.oConfiguration._collect();
this.sLegacyDateFormat = mChanges.legacyDateFormat = sFormatId;
this.setDatePattern("short", M_ABAP_DATE_FORMAT_PATTERN[sFormatId].pattern);
this.setDatePattern("medium", M_ABAP_DATE_FORMAT_PATTERN[sFormatId].pattern);
this.oConfiguration._endCollect();
return this;
},
/**
* Returns the currently set legacy ABAP time format (its id) or undefined if none has been set.
*
* @public
*/
getLegacyTimeFormat : function() {
return this.sLegacyTimeFormat || undefined;
},
/**
* Allows to specify one of the legacy ABAP time formats.
*
* This method sets the time patterns for 'short' and 'medium' style to the corresponding ABAP
* formats and sets the day period texts to "AM"/"PM" or "am"/"pm" respectively. When called
* with a null or undefined format id, any previously applied format will be removed.
*
* After changing the legacy time format, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {string} sFormatId id of the ABAP time format (one of '0','1','2','3','4')
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setLegacyTimeFormat : function(sFormatId) {
check(!sFormatId || M_ABAP_TIME_FORMAT_PATTERN.hasOwnProperty(sFormatId), "sFormatId must be one of ['0','1','2','3','4'] or empty");
var mChanges = this.oConfiguration._collect();
this.sLegacyTimeFormat = mChanges.legacyTimeFormat = sFormatId = sFormatId || "";
this.setTimePattern("short", M_ABAP_TIME_FORMAT_PATTERN[sFormatId]["short"]);
this.setTimePattern("medium", M_ABAP_TIME_FORMAT_PATTERN[sFormatId]["medium"]);
this._setDayPeriods("abbreviated", M_ABAP_TIME_FORMAT_PATTERN[sFormatId].dayPeriods);
this.oConfiguration._endCollect();
return this;
},
/**
* Returns the currently set legacy ABAP number format (its id) or undefined if none has been set.
*
* @public
*/
getLegacyNumberFormat : function() {
return this.sLegacyNumberFormat || undefined;
},
/**
* Allows to specify one of the legacy ABAP number format.
*
* This method will modify the 'group' and 'decimal' symbols. When called with a null
* or undefined format id, any previously applied format will be removed.
*
* After changing the legacy number format, the framework tries to update localization
* specific parts of the UI. See the documentation of {@link sap.ui.core.Configuration#setLanguage}
* for details and restrictions.
*
* @param {string} sFormatId id of the ABAP number format set (one of ' ','X','Y')
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setLegacyNumberFormat : function(sFormatId) {
sFormatId = sFormatId ? sFormatId.toUpperCase() : "";
check(!sFormatId || M_ABAP_NUMBER_FORMAT_SYMBOLS.hasOwnProperty(sFormatId), "sFormatId must be one of [' ','X','Y'] or empty");
var mChanges = this.oConfiguration._collect();
this.sLegacyNumberFormat = mChanges.legacyNumberFormat = sFormatId;
this.setNumberSymbol("group", M_ABAP_NUMBER_FORMAT_SYMBOLS[sFormatId].groupingSeparator);
this.setNumberSymbol("decimal", M_ABAP_NUMBER_FORMAT_SYMBOLS[sFormatId].decimalSeparator);
this.oConfiguration._endCollect();
return this;
},
/**
* Allows to specify the customizing data for Islamic calendar support
*
* @param {object[]} aMappings contains the customizing data for the support of Islamic calendar.
* @param {string} aMappings[].dateFormat The date format
* @param {string} aMappings[].islamicMonthStart The Islamic date
* @param {string} aMappings[].gregDate The corresponding Gregorian date
* @return {sap.ui.core.Configuration.FormatSettings} Returns <code>this</code> to allow method chaining
* @public
*/
setLegacyDateCalendarCustomizing : function(aMappings) {
check(jQuery.isArray(aMappings), "aMappings must be an Array");
var mChanges = this.oConfiguration._collect();
this.aLegacyDateCalendarCustomizing = mChanges.legacyDateCalendarCustomizing = aMappings;
this.oConfiguration._endCollect();
return this;
},
/**
* Returns the currently set customizing data for Islamic calendar support
*
* @return {object[]} Returns an array contains the customizing data. Each element in the array has properties: dateFormat, islamicMonthStart, gregDate. For details, please see {@link #setLegacyDateCalendarCustomizing}
* @public
*/
getLegacyDateCalendarCustomizing : function() {
return this.aLegacyDateCalendarCustomizing;
},
/*
* Returns a live object with the current settings
* TODO this method is part of the facade to be accessible from LocaleData, but it shouldn't be
* @private
*/
getCustomLocaleData : function() {
return this.mSettings;
}
});
return Configuration;
}, /* bExport= */ true);
},
"sap/ui/core/Control.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides base class sap.ui.core.Control for all controls
sap.ui.define(['jquery.sap.global', './CustomStyleClassSupport', './Element'],
function(jQuery, CustomStyleClassSupport, Element) {
"use strict";
//jQuery.sap.require("sap.ui.core.RenderManager"); // cyclic
/**
* Creates and initializes a new control with the given <code>sId</code> and settings.
*
* The set of allowed entries in the <code>mSettings</code> object depends on the concrete
* subclass and is described there. See {@link sap.ui.core.Element} for a general description of this
* argument.
*
* The settings supported by Control are:
* <ul>
* <li>Properties
* <ul>
* <li>{@link #getBusy busy} : boolean (default: false)</li>
* <li>{@link #getBusyIndicatorDelay busyIndicatorDelay} : int (default: 1000)</li>
* </ul>
* </li>
* </ul>
*
* @param {string} [sId] optional id for the new control; generated automatically if no non-empty id is given
* Note: this can be omitted, no matter whether <code>mSettings</code> will be given or not!
* @param {object} [mSettings] optional map/JSON-object with initial settings for the new control
* @public
*
* @class Base Class for Controls.
* @extends sap.ui.core.Element
* @abstract
* @author Martin Schaus, Daniel Brinkmann
* @version 1.28.5
* @alias sap.ui.core.Control
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var Control = Element.extend("sap.ui.core.Control", /* @lends sap.ui.core.Control */ {
metadata : {
stereotype : "control",
"abstract" : true,
publicMethods: ["placeAt", "attachBrowserEvent", "detachBrowserEvent"],
library: "sap.ui.core",
properties : {
/**
* Whether the control is currently in busy state.
*/
"busy" : {type: "boolean", defaultValue: false},
/**
* The delay in milliseconds, after which the busy indicator will show up for this control.
*/
"busyIndicatorDelay" : {type: "int", defaultValue: 1000},
/**
* Whether the control should be visible on the screen. If set to false, a placeholder is rendered instead of the real control
*/
"visible" : { type: "boolean", group : "Appearance", defaultValue: true }
}
},
constructor : function(sId, mSettings) {
// TODO initialization should happen in init
// but many of the existing controls don't call super.init()
// As a workaround I moved the initialization of bAllowTextSelection here
// so that it doesn't overwrite settings in init() (e.g. ListBox)
this.bAllowTextSelection = true;
Element.apply(this,arguments);
this.bOutput = this.getDomRef() != null; // whether this control has already produced output
if (this._sapUiCoreLocalBusy_initBusyIndicator) {
this._sapUiCoreLocalBusy_initBusyIndicator();
}
},
renderer : null // Control has no renderer
});
/**
* Overrides {@link sap.ui.core.Element#clone Element.clone} to clone additional
* internal state.
*
* The additionally cloned information contains:
* <ul>
* <li>browser event handlers attached with {@link #attachBrowserEvent}
* <li>text selection behavior
* <li>style classes added with {@link #addStyleClass}
* </ul>
*
* @param {string} [sIdSuffix] a suffix to be appended to the cloned element id
* @param {string[]} [aLocalIds] an array of local IDs within the cloned hierarchy (internally used)
* @return {sap.ui.core.Element} reference to the newly created clone
* @protected
*/
Control.prototype.clone = function() {
var oClone = Element.prototype.clone.apply(this, arguments);
if ( this.aBindParameters ) {
for (var i = 0, l = this.aBindParameters.length; i < l; i++) {
var aParams = this.aBindParameters[i];
oClone.attachBrowserEvent(aParams.sEventType, aParams.fnHandler, aParams.oListener !== this ? aParams.oListener : undefined);
}
}
oClone.bAllowTextSelection = this.bAllowTextSelection;
return oClone;
};
// must appear after clone() method and metamodel definition
CustomStyleClassSupport.apply(Control.prototype);
/**
* Checks whether the control is still active (part of the active DOM)
*
* @return {boolean} whether the control is still in the active DOM
* @private
*/
Control.prototype.isActive = function() {
return jQuery.sap.domById(this.sId) != null;
};
/**
* Triggers rerendering of this element and its children.
*
* As <code>sap.ui.core.Element</code> "bubbles up" the invalidate, changes to children
* potentially result in rerendering of the whole sub tree.
*
* @param {object} oOrigin
* @protected
*/
Control.prototype.invalidate = function(oOrigin) {
var oUIArea;
if ( this.bOutput && (oUIArea = this.getUIArea()) ) {
// if this control has been rendered before (bOutput)
// and if it is contained in an UIArea (!!oUIArea)
// then control re-rendering can be used (see UIArea.rerender() for details)
//
// The check for bOutput is necessary as the control
// re-rendering needs to identify the previous rendering results.
// Otherwise it wouldn't be able to replace them.
//
// Note about destroy(): when this control is currently in the process of being
// destroyed, registering it for an autonomous re-rendering doesn't make sense.
// In most cases, invalidation of the parent also doesn't make sense,
// but there might be composite controls that rely on being invalidated when
// a child is destroyed, so we keep the invalidation propagation untouched.
if ( !this._bIsBeingDestroyed ) {
oUIArea.addInvalidatedControl(this);
}
} else {
// else we bubble up the hierarchy
var oParent = this.getParent();
if (oParent && (
this.bOutput /* && !this.getUIArea() */ ||
/* !this.bOutput && */ !(this.getVisible && this.getVisible() === false))) {
// Note: the two comments in the condition above show additional conditions
// that help to understand the logic. As they are always fulfilled,
// they have been omitted for better performance.
//
// If this control has a parent but either
// - has produced output before ('this.bOutput') but is not part of an UIArea (!this.getUIArea())
// - or if it didn't produce output (!this.bOutput') before and is/became visible
// then invalidate the parent to request re-rendering
//
// The first commented condition is always true, otherwise the initial if condition
// in this method would have been met. The second one must be true as well because of the
// short evaluation logic of JavaScript. When bOutput is true the second half of the Or won't be processed.
oParent.invalidate(this);
}
}
};
/**
* Tries to replace its DOM reference by re-rendering.
* @protected
*/
Control.prototype.rerender = function() {
sap.ui.core.UIArea.rerenderControl(this);
};
/**
* Defines whether the user can select text inside this control.
* Defaults to <code>true</code> as long as this method has not been called.
*
* <b>Note:</b>This only works in IE and Safari; for Firefox the element's style must
* be set to:
* <pre>
* -moz-user-select: none;
* </pre>
* in order to prevent text selection.
*
* @param {boolean} bAllow whether to allow text selection or not
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
Control.prototype.allowTextSelection = function(bAllow) {
this.bAllowTextSelection = bAllow;
return this;
};
/**
* The string given as "sStyleClass" will be added to the "class" attribute of this control's root HTML element.
*
* This method is intended to be used to mark controls as being of a special type for which
* special styling can be provided using CSS selectors that reference this style class name.
*
* <pre>
* Example:
* myButton.addStyleClass("myRedTextButton"); // add a CSS class to one button instance
*
* ...and in CSS:
* .myRedTextButton {
* color: red;
* }
* </pre>
*
* This will add the CSS class "myRedTextButton" to the Button HTML and the CSS code above will then
* make the text in this particular button red.
*
* Only characters allowed inside HTML attributes are allowed.
* Quotes are not allowed and this method will ignore any strings containing quotes.
* Strings containing spaces are interpreted as ONE custom style class (even though CSS selectors interpret them
* as different classes) and can only removed later by calling removeStyleClass() with exactly the
* same (space-containing) string as parameter.
* Multiple calls with the same sStyleClass will have no different effect than calling once.
* If sStyleClass is null, the call is ignored.
*
* @name sap.ui.core.Control.prototype.addStyleClass
* @function
*
* @param {string} sStyleClass the CSS class name to be added
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
/**
* Removes the given string from the list of custom style classes that have been set previously.
* Regular style classes like "sapUiBtn" cannot be removed.
*
* @name sap.ui.core.Control.prototype.removeStyleClass
* @function
*
* @param {string} sStyleClass the style to be removed
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
/**
* The string given as "sStyleClass" will be be either added to or removed from the "class" attribute of this control's root HTML element,
* depending on the value of "bAdd": if bAdd is true, sStyleClass will be added.
* If bAdd is not given, sStyleClass will be removed if it is currently present and will be added if not present.
* If sStyleClass is null, the call is ignored.
*
* See addStyleClass and removeStyleClass for further documentation.
*
* @name sap.ui.core.Control.prototype.toggleStyleClass
* @function
*
* @param {string} sStyleClass the CSS class name to be added or removed
* @param {boolean} bAdd whether sStyleClass should be added (or removed); when this parameter is not given, sStyleClass will be toggled (removed, if present, and added if not present)
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
/**
* Returns true if the given style class string is valid and if this control has this style class set
* via a previous call to addStyleClass().
*
* @name sap.ui.core.Control.prototype.hasStyleClass
* @function
*
* @param {string} sStyleClass the style to check for
* @type boolean
* @return whether the given style has been set before
* @public
*/
/**
* Allows binding handlers for any native browser event to the root HTML element of this Control. This internally handles
* DOM element replacements caused by re-rendering.
*
* IMPORTANT:
* This should be only used as FALLBACK when the Control events do not cover a specific use-case! Always try using
* SAPUI5 control events, as e.g. accessibility-related functionality is then provided automatically.
* E.g. when working with a sap.ui.commons.Button, always use the Button's "press" event, not the native "click" event, because
* "press" is also guaranteed to be fired when certain keyboard activity is supposed to trigger the Button.
*
* In the event handler, "this" refers to the Control - not to the root DOM element like in jQuery. While the DOM element can
* be used and modified, the general caveats for working with SAPUI5 control DOM elements apply. In particular the DOM element
* may be destroyed and replaced by a new one at any time, so modifications that are required to have permanent effect may not
* be done. E.g. use Control.addStyleClass() instead if the modification is of visual nature.
*
* Use detachBrowserEvent() to remove the event handler(s) again.
*
* @param {string} [sEventType] A string containing one or more JavaScript event types, such as "click" or "blur".
* @param {function} [fnHandler] A function to execute each time the event is triggered.
* @param {object} [oListener] The object, that wants to be notified, when the event occurs
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
Control.prototype.attachBrowserEvent = function(sEventType, fnHandler, oListener) {
if (sEventType && (typeof (sEventType) === "string")) { // do nothing if the first parameter is empty or not a string
if (fnHandler && typeof (fnHandler) === "function") { // also do nothing if the second parameter is not a function
// store the parameters for bind()
if (!this.aBindParameters) {
this.aBindParameters = [];
}
oListener = oListener || this;
// FWE jQuery.proxy can't be used as it breaks our contract when used with same function but different listeners
var fnProxy = function() {
fnHandler.apply(oListener, arguments);
};
this.aBindParameters.push({
sEventType: sEventType,
fnHandler: fnHandler,
oListener: oListener,
fnProxy : fnProxy
});
if (!this._sapui_bInAfterRenderingPhase) {
// if control is rendered, directly call bind()
this.$().bind(sEventType, fnProxy);
}
}
}
return this;
};
/**
* Removes event handlers which have been previously attached using {@link #attachBrowserEvent}.
*
* Note: listeners are only removed, if the same combination of event type, callback function
* and context object is given as in the call to <code>attachBrowserEvent</code>.
*
* @param {string} [sEventType] A string containing one or more JavaScript event types, such as "click" or "blur".
* @param {function} [fnHandler] The function that is to be no longer executed.
* @param {object} [oListener] The context object that was given in the call to attachBrowserEvent.
* @public
*/
Control.prototype.detachBrowserEvent = function(sEventType, fnHandler, oListener) {
if (sEventType && (typeof (sEventType) === "string")) { // do nothing if the first parameter is empty or not a string
if (fnHandler && typeof (fnHandler) === "function") { // also do nothing if the second parameter is not a function
var $ = this.$(),i,oParamSet;
oListener = oListener || this;
// remove the bind parameters from the stored array
if (this.aBindParameters) {
for (i = this.aBindParameters.length - 1; i >= 0; i--) {
oParamSet = this.aBindParameters[i];
if ( oParamSet.sEventType === sEventType && oParamSet.fnHandler === fnHandler && oParamSet.oListener === oListener ) {
this.aBindParameters.splice(i, 1);
// if control is rendered, directly call unbind()
$.unbind(sEventType, oParamSet.fnProxy);
}
}
}
}
}
return this;
};
/**
* Returns a renderer for this control instance.
*
* It is retrieved using the RenderManager as done during rendering.
*
* @return {object} a Renderer suitable for this Control instance.
* @protected
*/
Control.prototype.getRenderer = function () {
//TODO introduce caching?
return sap.ui.core.RenderManager.getRenderer(this);
};
/**
* Puts <code>this</code> control into the specified container (<code>oRef</code>) at the given
* position (<code>oPosition</code>).
*
* First it is checked whether <code>oRef</code> is a container element / control (has a
* multiple aggregation with type <code>sap.ui.core.Control</code> and name 'content') or is an Id String
* of such an container.
* If this is not the case <code>oRef</code> can either be a Dom Reference or Id String of the UIArea
* (if it does not yet exist implicitly a new UIArea is created),
*
* The <code>oPosition</code> can be one of the following:
*
* <ul>
* <li>"first": The control is added as the first element to the container.</li>
* <li>"last": The control is added as the last element to the container (default).</li>
* <li>"only": All existing children of the container are removed (not destroyed!) and the control is added as new child.</li>
* <li><i>index</i>: The control is added at the specified <i>index</i> to the container.</li>
* </ul>
*
* @param {string|Element|sap.ui.core.Control} oRef container into which the control should be put
* @param {string|int} oPosition Describes the position where the control should be put into the container
* @return {sap.ui.core.Control} Returns <code>this</code> to allow method chaining
* @public
*/
Control.prototype.placeAt = function(oRef, oPosition) {
var oCore = sap.ui.getCore();
if (oCore.isInitialized()) {
// core already initialized, do it now
// 1st try to resolve the oRef as a Container control
var oContainer = oRef;
if (typeof oContainer === "string") {
oContainer = oCore.byId(oRef);
}
// if no container control is found use the corresponding UIArea
var bIsUIArea = false;
if (!(oContainer instanceof Element)) {
oContainer = oCore.createUIArea(oRef);
bIsUIArea = true;
}
if (!oContainer) {
return this;
}
if (!bIsUIArea) {
var oContentAggInfo = oContainer.getMetadata().getAggregation("content");
var bContainerSupportsPlaceAt = true;
if (oContentAggInfo) {
if (!oContentAggInfo.multiple || oContentAggInfo.type != "sap.ui.core.Control") {
bContainerSupportsPlaceAt = false;
}
} else {
//Temporary workaround for sap.ui.commons.AbsoluteLayout to enable placeAt even when no content aggregation is available. TODO: Find a proper solution
if (!oContainer.addContent || !oContainer.insertContent || !oContainer.removeAllContent) {
bContainerSupportsPlaceAt = false;
}
}
if (!bContainerSupportsPlaceAt) {
jQuery.sap.log.warning("placeAt cannot be processed because container " + oContainer + " does not have an aggregation 'content'.");
return this;
}
}
if (typeof oPosition === "number") {
oContainer.insertContent(this, oPosition);
} else {
oPosition = oPosition || "last"; //"last" is default
switch (oPosition) {
case "last":
oContainer.addContent(this);
break;
case "first":
oContainer.insertContent(this, 0);
break;
case "only":
oContainer.removeAllContent();
oContainer.addContent(this);
break;
default:
jQuery.sap.log.warning("Position " + oPosition + " is not supported for function placeAt.");
}
}
} else {
// core not yet initialized, defer execution
var that = this;
oCore.attachInitEvent(function () {
that.placeAt(oRef, oPosition);
});
}
return this;
};
/*
* Event handling
*/
/**
* Cancels user text selection if text selection is disabled for this control.
* See the {@link #allowTextSelection} method.
* @private
*/
Control.prototype.onselectstart = function (oBrowserEvent) {
if (!this.bAllowTextSelection) {
oBrowserEvent.preventDefault();
oBrowserEvent.stopPropagation();
}
};
/*
* Rendering
*/
/**
* Function is called before the rendering of the control is started.
*
* Applications must not call this hook method directly, it is called by the framework.
*
* Subclasses of Control should override this hook to implement any necessary actions before the rendering.
*
* @function
* @name sap.ui.core.Control.prototype.onBeforeRendering
* @protected
*/
//sap.ui.core.Control.prototype.onBeforeRendering = function() {};
/**
* Function is called when the rendering of the control is completed.
*
* Applications must not call this hook method directly, it is called by the framework.
*
* Subclasses of Control should override this hook to implement any necessary actions after the rendering.
*
* @function
* @name sap.ui.core.Control.prototype.onAfterRendering
* @protected
*/
//sap.ui.core.Control.prototype.onAfterRendering = function() {};
/**
* Returns the DOMNode Id to be used for the "labelFor" attribute of the label.
*
* By default, this is the Id of the control itself.
*
* @return {string} Id to be used for the <code>labelFor</code>
* @public
*/
Control.prototype.getIdForLabel = function () {
return this.getId();
};
Control.prototype.destroy = function(bSuppressInvalidate) {
// avoid rerendering
this._bIsBeingDestroyed = true;
//Cleanup Busy Indicator
this._cleanupBusyIndicator();
sap.ui.core.ResizeHandler.deregisterAllForControl(this.getId());
Element.prototype.destroy.call(this, bSuppressInvalidate);
};
(function() {
var sPreventedEvents = "focusin focusout keydown keypress keyup mousedown touchstart mouseup touchend click",
oBusyIndicatorDelegate = {
onAfterRendering: function() {
if (this.getProperty("busy") === true && this.$()) {
fnAppendBusyIndicator.apply(this);
}
}
},
fnAppendBusyIndicator = function() {
var $this = this.$(this._sBusySection),
aForbiddenTags = ["area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"];
//If there is a pending delayed call to append the busy indicator, we can clear it now
if (this._busyIndicatorDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyIndicatorDelayedCallId);
delete this._busyIndicatorDelayedCallId;
}
//Check if DOM Element where the busy indicator is supposed to be placed can handle content
var sTag = $this.get(0) && $this.get(0).tagName;
if (sTag && jQuery.inArray(sTag.toLowerCase(), aForbiddenTags) >= 0) {
jQuery.sap.log.warning("Busy Indicator cannot be placed in elements with tag " + sTag);
return;
}
//check if the control has static position, if this is the case we need to change it,
//because we relay on relative/absolute/fixed positioning
if ($this.css('position') == 'static') {
this._busyStoredPosition = 'static';
$this.css('position', 'relative');
}
//Append busy indicator to control DOM
var $BusyIndicator = jQuery('<div class="sapUiLocalBusyIndicator"><div class="sapUiLocalBusyIndicatorAnimation"><div class="sapUiLocalBusyIndicatorBox"></div><div class="sapUiLocalBusyIndicatorBox"></div><div class="sapUiLocalBusyIndicatorBox"></div></div></div>');
$BusyIndicator.attr("id",this.getId() + "-busyIndicator");
$this.append($BusyIndicator);
$this.addClass('sapUiLocalBusy');
if (this._busyDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyDelayedCallId);
}
this._busyDelayedCallId = jQuery.sap.delayedCall(1200, this, fnAnimate);
fnHandleInteraction.apply(this, [true]);
},
fnHandleInteraction = function(bBusy) {
var $this = this.$(this._sBusySection);
if (bBusy) {
var $TabRefs = $this.find('[tabindex]'),
that = this;
this._busyTabIndices = [];
// if only the control itself without any tabrefs was found
// block the events as well
this._busyTabIndices.push({
ref : $this,
tabindex : $this.attr('tabindex')
});
$this.attr('tabindex', -1);
$this.bind(sPreventedEvents, fnPreserveEvents);
$TabRefs.each(function(iIndex, oObject) {
var $Ref = jQuery(oObject),
iTabIndex = $Ref.attr('tabindex');
if (iTabIndex < 0) {
return true;
}
that._busyTabIndices.push({
ref: $Ref,
tabindex: iTabIndex
});
$Ref.attr('tabindex', -1);
$Ref.bind(sPreventedEvents, fnPreserveEvents);
});
} else {
if (this._busyTabIndices) {
jQuery.each(this._busyTabIndices, function(iIndex, oObject) {
oObject.ref.attr('tabindex', oObject.tabindex);
oObject.ref.unbind(sPreventedEvents, fnPreserveEvents);
});
}
this._busyTabIndices = [];
}
},
fnPreserveEvents = function(oEvent) {
oEvent.preventDefault();
oEvent.stopImmediatePropagation();
},
fnAnimate = function() {
var $bubbles = this.$(this._sBusySection).children('.sapUiLocalBusyIndicator').children('.sapUiLocalBusyIndicatorAnimation');
var that = this;
that._busyAnimationTimer1 = setTimeout(function() {
$bubbles.children(":eq(0)").addClass('active');
$bubbles.children(":not(:eq(0))").removeClass('active');
that._busyAnimationTimer2 = setTimeout(function() {
$bubbles.children(":eq(1)").addClass('active');
$bubbles.children(":not(:eq(1))").removeClass('active');
that._busyAnimationTimer3 = setTimeout(function() {
$bubbles.children(":eq(2)").addClass('active');
$bubbles.children(":not(:eq(2))").removeClass('active');
that._busyAnimationTimer4 = setTimeout(function() {
$bubbles.children().removeClass('active');
}, 150);
}, 150);
}, 150);
}, 150);
this._busyDelayedCallId = jQuery.sap.delayedCall(1200, this, fnAnimate);
};
/**
* Set the controls busy state.
*
* @param {boolean} bBusy The new busy state to be set
* @return {sap.ui.core.Control} <code>this</code> to allow method chaining
* @public
*/
Control.prototype.setBusy = function (bBusy, sBusySection /* this is an internal parameter to apply partial local busy indicator for a specific section of the control */) {
this._sBusySection = sBusySection;
var $this = this.$(this._sBusySection);
//If the new state is already set, we don't need to do anything
if (bBusy == this.getProperty("busy")) {
return;
}
//No rerendering
this.setProperty("busy", bBusy, true);
if (bBusy) {
this.addDelegate(oBusyIndicatorDelegate, false, this);
} else {
this.removeDelegate(oBusyIndicatorDelegate);
//If there is a pending delayed call we clear it
if (this._busyIndicatorDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyIndicatorDelayedCallId);
delete this._busyIndicatorDelayedCallId;
}
}
//If no domref exists stop here.
if (!this.getDomRef()) {
return;
}
if (bBusy) {
if (this.getBusyIndicatorDelay() <= 0) {
fnAppendBusyIndicator.apply(this);
} else {
this._busyIndicatorDelayedCallId = jQuery.sap.delayedCall(this.getBusyIndicatorDelay(), this, fnAppendBusyIndicator);
}
} else {
//Remove the busy indicator from the DOM
this.$("busyIndicator").remove();
this.$().removeClass('sapUiLocalBusy');
//Reset the position style to its original state
if (this._busyStoredPosition) {
$this.css('position', this._busyStoredPosition);
delete this._busyStoredPosition;
}
fnHandleInteraction.apply(this, [false]);
if (this._busyDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyDelayedCallId);
delete this._busyDelayedCallId;
}
}
};
/**
* Check if the control is currently in busy state
*
* @public
* @deprecated Use getBusy instead
* @return boolean
*/
Control.prototype.isBusy = function() {
return this.getProperty("busy");
};
/**
* Define the delay, after which the busy indicator will show up
*
* @public
* @param {int} iDelay The delay in ms
* @return {sap.ui.core.Control} <code>this</code> to allow method chaining
*/
Control.prototype.setBusyIndicatorDelay = function(iDelay) {
this.setProperty("busyIndicatorDelay", iDelay, true);
return this;
};
/**
* Cleanup all timers which might have been created by the busy indicator
*
* @private
*/
Control.prototype._cleanupBusyIndicator = function() {
if (this._busyIndicatorDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyIndicatorDelayedCallId);
delete this._busyIndicatorDelayedCallId;
}
if (this._busyDelayedCallId) {
jQuery.sap.clearDelayedCall(this._busyDelayedCallId);
delete this._busyDelayedCallId;
}
if (this._busyAnimationTimer1) {
clearTimeout(this._busyAnimationTimer1);
delete this._busyAnimationTimer1;
}
if (this._busyAnimationTimer2) {
clearTimeout(this._busyAnimationTimer2);
delete this._busyAnimationTimer2;
}
if (this._busyAnimationTimer3) {
clearTimeout(this._busyAnimationTimer3);
delete this._busyAnimationTimer3;
}
if (this._busyAnimationTimer4) {
clearTimeout(this._busyAnimationTimer4);
delete this._busyAnimationTimer4;
}
};
})();
return Control;
}, /* bExport= */ true);
},
"sap/ui/core/Core.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the real core class sap.ui.core.Core of SAPUI5
sap.ui.define(['jquery.sap.global', 'sap/ui/Device', 'sap/ui/Global', 'sap/ui/base/DataType', 'sap/ui/base/EventProvider', './Component', './Configuration', './Control', './Element', './ElementMetadata', './FocusHandler', './RenderManager', './ResizeHandler', './ThemeCheck', './UIArea', './tmpl/Template', './message/MessageManager', 'jquery.sap.act', 'jquery.sap.dom', 'jquery.sap.events', 'jquery.sap.mobile', 'jquery.sap.properties', 'jquery.sap.resources', 'jquery.sap.script'],
function(jQuery, Device, Global, DataType, EventProvider, Component, Configuration, Control, Element, ElementMetadata, FocusHandler, RenderManager, ResizeHandler, ThemeCheck, UIArea, Template, MessageManager/* , jQuerySap6, jQuerySap, jQuerySap1, jQuerySap2, jQuerySap3, jQuerySap4, jQuerySap5 */) {
"use strict";
/*global Promise */
/**
* Set of libraries that have been loaded and initialized already.
* This is maintained separately from Core.mLibraries to protect it against
* modification from the outside (objects in mLibraries are currently exposed
* by getLoadedLibraries())
*/
var mLoadedLibraries = {};
/**
* @class Core Class of the SAP UI Library.
*
* This class boots the Core framework and makes it available for the Application
* via the method <code>sap.ui.getCore()</code>.
*
* Example:<br/>
* <pre> var oCore = sap.ui.getCore();</pre><br/>
*
* It provides events where the Application can attach to.<br/>
* Example:<br/>
* <pre>
* oCore.attachInit(function () {
* //do the needful, do it lean
* });
* </pre><br/>
*
* It registers the Browser Eventing.
*
* @extends sap.ui.base.EventProvider
* @final
* @author SAP SE
* @version 1.28.5
* @constructor
* @alias sap.ui.core.Core
* @public
*/
var Core = EventProvider.extend("sap.ui.core.Core", /** @lends sap.ui.core.Core.prototype */ {
constructor : function() {
//make this class only available once
if (sap.ui.getCore && sap.ui.getCore()) {
return sap.ui.getCore();
}
var that = this,
log = jQuery.sap.log,
METHOD = "sap.ui.core.Core";
//inheritance to be able to fire internal events
EventProvider.apply(this);
/**
* Whether the core has been booted
* @private
*/
this.bBooted = false;
/**
* Whether the core has been initialized
* @private
*/
this.bInitialized = false;
/**
* Whether the dom is ready (document.ready)
* @private
*/
this.bDomReady = false;
/**
* Available plugins in the order of registration.
* @private
*/
this.aPlugins = [];
/**
* Collection of loaded or adhoc created libraries, keyed by their name.
* @private
*/
this.mLibraries = {};
/**
* Already loaded resource bundles keyed by library and locale.
* @private
* @see sap.ui.core.Core.getLibraryResourceBundle
*/
this.mResourceBundles = {};
/**
* Currently created UIAreas keyed by their id.
* @private
* @todo FIXME how can a UI area ever be removed?
*/
this.mUIAreas = {};
/**
* Default model used for databinding
* @private
*/
this.oModels = {};
/**
* The event bus (initialized lazily)
* @private
*/
this.oEventBus = null;
/**
* Map of of created Elements keyed by their id.
*
* Each element registers itself in its constructor and deregisters itself in its
* destroy method.
*
* @private
* @todo get rid of this collection as it represents a candidate for memory leaks
*/
this.mElements = {};
/**
* Map of of created objects structured by their type which contains a map
* containing the created objects keyed by their type.
*
* Each object registers itself in its constructor and deregisters itself in its
* destroy method.
*
* @private
* @todo get rid of this collection as it represents a candidate for memory leaks
*/
this.mObjects = {
"component": {},
"template": {}
};
/**
* The instance of the root component (defined in the configuration {@link sap.ui.core.Configuration#getRootComponent})
* @private
*/
this.oRootComponent = null;
/**
* Ordered collection of initEvent listeners
* Moved here (before boot()) so that the libraries can be registered for lazy load!!
* @private
*/
this.aInitListeners = [];
/**
* Whether the legacy library has to be loaded.
* @private
*/
this.bInitLegacyLib = false;
log.info("Creating Core",null,METHOD);
/**
* Object holding the interpreted configuration
* Initialized from the global "sap-ui-config" object and from Url parameters
* @private
*/
this.oConfiguration = new Configuration(this);
// initialize frameOptions script (anti-clickjacking, ect.)
var oFrameOptionsConfig = this.oConfiguration["frameOptionsConfig"] || {};
oFrameOptionsConfig.mode = this.oConfiguration["frameOptions"];
oFrameOptionsConfig.whitelistService = this.oConfiguration["whitelistService"];
this.oFrameOptions = new jQuery.sap.FrameOptions(oFrameOptionsConfig);
// enable complex bindings if configured
if ( this.oConfiguration["bindingSyntax"] === "complex" ) {
sap.ui.base.ManagedObject.bindingParser = sap.ui.base.BindingParser.complexParser;
}
// switch bindingParser to designTime mode if configured
if (this.oConfiguration["xx-designMode"] == true ) {
sap.ui.base.BindingParser._keepBindingStrings = true;
}
// grant ElementMetadata "friend" access to Core for registration
ElementMetadata.prototype.register = function(oMetadata) {
that.registerElementClass(oMetadata);
};
// grant Element "friend" access to Core for (de-)registration
Element.prototype.register = function() {
that.registerElement(this);
};
Element.prototype.deregister = function() {
that.deregisterElement(this);
};
// grant Component "friend" access to Core for (de-)registration
Component.prototype.register = function() {
that.registerObject(this);
};
Component.prototype.deregister = function() {
that.deregisterObject(this);
};
// grant Template "friend" access to Core for (de-)registration
Template.prototype.register = function() {
that.registerObject(this);
};
Template.prototype.deregister = function() {
that.deregisterObject(this);
};
// handle modules
var aModules = this.oConfiguration.modules;
if ( this.oConfiguration.getDebug() ) {
// add debug module if configured
aModules.unshift("sap.ui.debug.DebugEnv");
}
// enforce the core library as the first loaded module
var i = jQuery.inArray("sap.ui.core.library", aModules);
if ( i != 0 ) {
if ( i > 0 ) {
aModules.splice(i,1);
}
aModules.unshift("sap.ui.core.library");
}
// enable LessSupport if specified in configuration
if (this.oConfiguration["xx-lesssupport"] && jQuery.inArray("sap.ui.core.plugin.LessSupport", aModules) == -1) {
log.info("Including LessSupport into declared modules");
aModules.push("sap.ui.core.plugin.LessSupport");
}
log.info("Declared modules: " + aModules, METHOD);
var oCfgData = window["sap-ui-config"];
// Configuration might have a themeRoot, if so integrate it in themeroots
if ( this.oConfiguration.themeRoot ) {
oCfgData = oCfgData || {};
oCfgData.themeroots = oCfgData.themeroots || {};
oCfgData.themeroots[this.oConfiguration.getTheme()] = this.oConfiguration.themeRoot;
}
if (oCfgData) {
// read themeRoots configuration
if (oCfgData.themeroots) {
for (var themeName in oCfgData.themeroots) {
var themeRoot = oCfgData.themeroots[themeName];
if (typeof themeRoot === "string") {
this.setThemeRoot(themeName, themeRoot);
} else {
for (var lib in themeRoot) {
if (lib.length > 0) {
this.setThemeRoot(themeName, [lib], themeRoot[lib]);
} else {
this.setThemeRoot(themeName, themeRoot[lib]);
}
}
}
}
}
}
// set CSS class for the theme name
this.sTheme = this.oConfiguration.getTheme();
jQuery(document.documentElement).addClass("sapUiTheme-" + this.sTheme);
log.info("Declared theme " + this.sTheme,null,METHOD);
if (this.oConfiguration.getRTL()) {
jQuery(document.documentElement).attr("dir", "rtl"); // webkit does not allow setting document.dir before the body exists
log.info("RTL mode activated",null,METHOD);
}
//set the browser for css attribute selectors. do not move this to the onload function because sf and ie do not
//use the classes
var $html = jQuery("html");
var b = Device.browser;
var id = b.name;
if (id === b.BROWSER.CHROME) {
jQuery.browser.safari = false;
jQuery.browser.chrome = true;
} else if (id === b.BROWSER.SAFARI) {
jQuery.browser.safari = true;
jQuery.browser.chrome = false;
if (b.mobile) {
id = "m" + id;
}
}
if (id) {
jQuery.browser.fVersion = b.version;
jQuery.browser.mobile = b.mobile;
id = id + Math.floor(b.version);
$html.attr("data-sap-ui-browser", id);
log.debug("Browser-Id: " + id, null, METHOD);
}
$html.attr("data-sap-ui-os", Device.os.name + Device.os.versionStr);
var osCSS = null;
switch (Device.os.name) {
case Device.os.OS.IOS:
osCSS = "sap-ios";
break;
case Device.os.OS.ANDROID:
osCSS = "sap-android";
break;
case Device.os.OS.BLACKBERRY:
osCSS = "sap-bb";
break;
case Device.os.OS.WINDOWS_PHONE:
osCSS = "sap-winphone";
break;
}
if (osCSS) {
$html.addClass(osCSS);
}
// append the lang info to the document (required for ARIA support)
var fnUpdateLangAttr = function() {
var oLocale = this.oConfiguration.getLocale();
if (oLocale) {
$html.attr("lang", oLocale.toString());
} else {
$html.removeAttr("lang");
}
};
fnUpdateLangAttr.call(this);
// listen to localization change event to update the lang info
this.attachLocalizationChanged(fnUpdateLangAttr, this);
//if weinre id is set, load weinre target script
if (this.oConfiguration.getWeinreId()) {
log.info("Starting WEINRE Remote Web Inspector");
var sWeinreScript = "<script src=\"";
sWeinreScript += this.oConfiguration.getWeinreServer();
sWeinreScript += "/target/target-script-min.js#";
sWeinreScript += jQuery.sap.encodeURL(this.oConfiguration.getWeinreId());
sWeinreScript += "\"></script>";
document.write(sWeinreScript);
}
// create accessor to the Core API early so that initLibrary and others can use it
/**
* Retrieve the {@link sap.ui.core.Core SAPUI5 Core} instance for the current window.
* @returns {sap.ui.core.Core} the API of the current SAPUI5 Core instance.
* @public
* @function
*/
sap.ui.getCore = jQuery.sap.getter(this.getInterface());
// create the RenderManager so it can be used already
this.oRenderManager = new RenderManager();
// sync point 1 synchronizes document ready and rest of UI5 boot
var oSyncPoint1 = jQuery.sap.syncPoint("UI5 Document Ready", function(iOpenTasks, iFailures) {
that.handleLoad();
});
var iDocumentReadyTask = oSyncPoint1.startTask("document.ready");
var iCoreBootTask = oSyncPoint1.startTask("preload and boot");
// task 1 is to wait for document.ready
jQuery(function() {
log.trace("document is ready");
oSyncPoint1.finishTask(iDocumentReadyTask);
});
// sync point 2 synchronizes all preload script loads and the end of the bootstrap script
var oSyncPoint2 = jQuery.sap.syncPoint("UI5 Core Preloads and Bootstrap Script", function(iOpenTasks, iFailures) {
log.trace("Core loaded: open=" + iOpenTasks + ", failures=" + iFailures);
that._boot();
oSyncPoint1.finishTask(iCoreBootTask);
});
// when a boot task is configured, add it to syncpoint2
var fnCustomBootTask = this.oConfiguration["xx-bootTask"];
if ( fnCustomBootTask ) {
var iCustomBootTask = oSyncPoint2.startTask("custom boot task");
fnCustomBootTask( function(bSuccess) {
oSyncPoint2.finishTask(iCustomBootTask, typeof bSuccess === "undefined" || bSuccess === true );
});
}
/**
* Whether the current browser needs a polyfill as a fallback for flex box support
* @type {boolean}
* @private
* @name jQuery.support.useFlexBoxPolyfill
* @since 1.12.0
* @deprecated since version 1.16.0
*
* For backwards compatibility we can't remove the deprecated flexbox polyfill.
* However, if the compatibility version is 1.16 or higher then the polyfill
* should not be used.
*/
var useFlexBoxPolyfillCompatVersion = new jQuery.sap.Version(this.oConfiguration.getCompatibilityVersion("flexBoxPolyfill"));
// Always false if version is >= 1.16
if (useFlexBoxPolyfillCompatVersion.compareTo("1.16") >= 0) {
jQuery.support.useFlexBoxPolyfill = false;
} else if (!jQuery.support.flexBoxLayout && !jQuery.support.newFlexBoxLayout && !jQuery.support.ie10FlexBoxLayout) {
jQuery.support.useFlexBoxPolyfill = true;
} else {
jQuery.support.useFlexBoxPolyfill = false;
}
// when the bootstrap script has finished, it calls sap.ui.getCore().boot()
var iBootstrapScriptTask = oSyncPoint2.startTask("bootstrap script");
this.boot = function() {
if (this.bBooted) {
return;
}
this.bBooted = true;
oSyncPoint2.finishTask(iBootstrapScriptTask);
};
// determine preload mode (e.g. resolve default or auto)
var sPreloadMode = this.oConfiguration.preload;
// if debug sources are requested, then the preload feature must be deactivated
if ( window["sap-ui-debug"] ) {
sPreloadMode = "";
}
// when the preload mode is 'auto', it will be set to 'sync' for optimized sources
if ( sPreloadMode === "auto" ) {
sPreloadMode = (window["sap-ui-optimized"] && !this.oConfiguration['xx-loadAllMode']) ? "sync" : "";
}
// write back the determined mode for later evaluation (e.g. loadLibrary)
this.oConfiguration.preload = sPreloadMode;
if ( sPreloadMode === "sync" || sPreloadMode === "async" ) {
var bAsyncPreload = sPreloadMode !== "sync";
jQuery.each(aModules, function(i,sModule) {
if ( sModule.match(/\.library$/) ) {
// Note: in async mode, all preloads together contribute to oSyncPoint2.
// Only after that SP2 has been reached, library modules will be required by the Core.
jQuery.sap.preloadModules(sModule + "-preload", bAsyncPreload, oSyncPoint2);
}
});
}
// initializes the application cachebuster mechanism if configured
var aACBConfig = this.oConfiguration.getAppCacheBuster();
if (aACBConfig && aACBConfig.length > 0) {
jQuery.sap.require("sap.ui.core.AppCacheBuster");
sap.ui.core.AppCacheBuster.boot(oSyncPoint2);
}
},
metadata : {
publicMethods: ["boot", "isInitialized","isThemeApplied","attachInitEvent","attachInit","getRenderManager","createRenderManager",
"getConfiguration", "setRoot", "createUIArea", "getUIArea", "getUIDirty", "getElementById",
"getCurrentFocusedControlId", "getControl", "getComponent", "getTemplate", "lock", "unlock","isLocked",
"attachEvent","detachEvent","applyChanges", "getEventBus",
"applyTheme","setThemeRoot","attachThemeChanged","detachThemeChanged","getStaticAreaRef",
"registerPlugin","unregisterPlugin","getLibraryResourceBundle", "byId",
"getLoadedLibraries", "loadLibrary", "loadLibraries", "initLibrary",
"includeLibraryTheme", "setModel", "getModel", "hasModel", "isMobile",
"attachControlEvent", "detachControlEvent", "attachIntervalTimer", "detachIntervalTimer",
"attachParseError", "detachParseError", "fireParseError",
"attachValidationError", "detachValidationError", "fireValidationError",
"attachFormatError", "detachFormatError", "fireFormatError",
"attachValidationSuccess", "detachValidationSuccess", "fireValidationSuccess",
"attachLocalizationChanged", "detachLocalizationChanged",
"attachLibraryChanged", "detachLibraryChanged",
"isStaticAreaRef", "createComponent", "getRootComponent", "getApplication",
"setMessageManager", "getMessageManager"]
}
});
/**
* Map of event names and ids, that are provided by this class
* @private
*/
Core.M_EVENTS = {ControlEvent: "ControlEvent", UIUpdated: "UIUpdated", ThemeChanged: "ThemeChanged", LocalizationChanged: "localizationChanged",
LibraryChanged : "libraryChanged",
ValidationError : "validationError", ParseError : "parseError", FormatError : "formatError", ValidationSuccess : "validationSuccess"};
// Id of the static UIArea
var STATIC_UIAREA_ID = "sap-ui-static";
/**
* Boots the core and injects the necessary css and js files for the library.
* Applications shouldn't call this method. It is automatically called by the bootstrap scripts (e.g. sap-ui-core.js)
*
* @private
*/
Core.prototype._boot = function() {
//do not allow any event processing until the Core is booting
this.lock();
// if a list of preloaded library CSS is configured, request a merged CSS (if application did not already do it)
var aCSSLibs = this.oConfiguration['preloadLibCss'];
if ( aCSSLibs.length > 0 ) {
// a leading "!" denotes that the application has loaded the file already
var bAppManaged = aCSSLibs[0].slice(0,1) === "!";
if ( bAppManaged ) {
aCSSLibs[0] = aCSSLibs[0].slice(1); // also affect same array in this.oConfiguration!
}
if ( aCSSLibs[0] === "*" ) {
// replace with configured libs
aCSSLibs.splice(0,1); // remove *
var pos = 0;
jQuery.each(this.oConfiguration.modules, function(i,mod) {
var m = mod.match(/^(.*)\.library$/);
if ( m ) {
aCSSLibs.splice(pos,0,m[1]);
}
});
}
if ( !bAppManaged ) {
this.includeLibraryTheme("sap-ui-merged", undefined, "?l=" + aCSSLibs.join(","));
}
}
// load all modules now
var that = this;
jQuery.each(this.oConfiguration.modules, function(i,mod) {
var m = mod.match(/^(.*)\.library$/);
if ( m ) {
that.loadLibrary(m[1]);
} else {
jQuery.sap.require(mod);
}
});
//allow events again
this.unlock();
};
/**
* Applies the theme with the given name (by loading the respective style sheets, which does not disrupt the application).
*
* By default, the theme files are expected to be located at path relative to the respective control library ([libraryLocation]/themes/[themeName]).
* Different locations can be configured by using the method setThemePath() or by using the second parameter "sThemeBaseUrl" of applyTheme().
* Usage of this second parameter is a shorthand for setThemePath and internally calls setThemePath, so the theme location is then known.
*
* sThemeBaseUrl is a single URL to specify the default location of all theme files. This URL is the base folder below which the control library folders
* are located. E.g. if the CSS files are not located relative to the root location of UI5, but instead they are at locations like
* http://my.server/myapp/resources/sap/ui/core/themes/my_theme/library.css
* then the URL that needs to be given is:
* http://my.server/myapp/resources
* All theme resources are then loaded from below this folder - except if for a certain library a different location has been registered.
*
* If the theme resources are not all either below this base location or with their respective libraries, then setThemePath must be
* used to configure individual locations.
*
* @param {string} sThemeName the name of the theme to be loaded
* @param {string} [sThemeBaseUrl] the (optional) base location of the theme
* @public
*/
Core.prototype.applyTheme = function(sThemeName, sThemeBaseUrl) {
jQuery.sap.assert(typeof sThemeName === "string", "sThemeName must be a string");
jQuery.sap.assert(typeof sThemeBaseUrl === "string" || typeof sThemeBaseUrl === "undefined", "sThemeBaseUrl must be a string or undefined");
sThemeName = this.oConfiguration._normalizeTheme(sThemeName, sThemeBaseUrl);
if (sThemeBaseUrl) {
this.setThemeRoot(sThemeName, sThemeBaseUrl);
}
// only apply the theme if it is different from the active one
if (sThemeName && this.sTheme != sThemeName) {
var sCurrentTheme = this.sTheme;
this._updateThemeUrls(sThemeName);
this.sTheme = sThemeName;
this.oConfiguration._setTheme(sThemeName);
// modify the <html> tag's CSS class with the theme name
jQuery(document.documentElement).removeClass("sapUiTheme-" + sCurrentTheme).addClass("sapUiTheme-" + sThemeName);
// notify the listeners
if ( this.oThemeCheck ) {
this.oThemeCheck.fireThemeChangedEvent(false, true);
}
}
};
// modify style sheet URLs to point to the given theme, using the current RTL mode
Core.prototype._updateThemeUrls = function(sThemeName) {
var that = this,
sRTL = this.oConfiguration.getRTL() ? "-RTL" : "";
// select "our" stylesheets
jQuery("link[id^=sap-ui-theme-]").each(function() {
var sLibName = this.id.slice(13), // length of "sap-ui-theme-"
sLibFileName = this.href.slice(this.href.lastIndexOf("/") + 1),
sStandardLibFilePrefix = "library",
sHref,
pos,
$this = jQuery(this);
// handle 'variants'
if ((pos = sLibName.indexOf("-[")) > 0) { // assumes that "-[" does not occur as part of a library name
sStandardLibFilePrefix += sLibName.slice(pos + 2, -1); // 2=length of "-]"
sLibName = sLibName.slice(0, pos);
}
// try to distinguish "our" library css from custom css included with the ':' notation in includeLibraryTheme
if ( sLibFileName === (sStandardLibFilePrefix + ".css") || sLibFileName === (sStandardLibFilePrefix + "-RTL.css") ) {
sLibFileName = sStandardLibFilePrefix + sRTL + ".css";
}
// remove additional css files (ie9 rule limit fix)
if ($this.attr("sap-ui-css-count")) {
$this.remove();
}
// set new URL
sHref = that._getThemePath(sLibName, sThemeName) + sLibFileName;
if ( sHref != this.href ) {
this.href = sHref;
$this.removeAttr("sap-ui-ready");
}
});
};
/**
* Returns the URL of the folder in which the CSS file for the given theme and the given library is located .
* The returned URL ends with a slash.
*
* @param sLibName
* @param sThemeName
* @private
*/
Core.prototype._getThemePath = function(sLibName, sThemeName) {
if (this._mThemeRoots) {
var path = this._mThemeRoots[sThemeName + " " + sLibName] || this._mThemeRoots[sThemeName];
// check whether for this combination (theme+lib) a URL is registered or for this theme a default location is registered
if (path) {
path = path + sLibName.replace(/\./g, "/") + "/themes/" + sThemeName + "/";
jQuery.sap.registerModulePath(sLibName + ".themes." + sThemeName, path);
return path;
}
}
// use the library location as theme location
return jQuery.sap.getModulePath(sLibName + ".themes." + sThemeName, "/");
};
/**
* Defines the root directory from below which UI5 should load the theme with the given name.
* Optionally allows restricting the setting to parts of a theme covering specific control libraries.
*
* Example:
* <code>
* core.setThemeRoot("my_theme", "http://mythemeserver.com/allThemes");
* core.applyTheme("my_theme");
* </code>
* will cause the following file to be loaded:
* <code>http://mythemeserver.com/allThemes/sap/ui/core/themes/my_theme/library.css</code>
* (and the respective files for all used control libraries, like <code>http://mythemeserver.com/allThemes/sap/ui/commons/themes/my_theme/library.css</code>
* if the sap.ui.commons library is used)
*
* If parts of the theme are at different locations (e.g. because you provide a standard theme like "sap_goldreflection" for a custom control library and
* this self-made part of the standard theme is at a different location than the UI5 resources), you can also specify for which control libraries the setting
* should be used, by giving an array with the names of the respective control libraries as second parameter:
* <code>core.setThemeRoot("sap_goldreflection", ["my.own.library"], "http://mythemeserver.com/allThemes");</code>
* This will cause the Gold Reflection theme to be loaded normally from the UI5 location, but the part for styling the "my.own.library" controls will be loaded from:
* <code>http://mythemeserver.com/allThemes/my/own/library/themes/sap_goldreflection/library.css</code>
*
* If the custom theme should be loaded initially (via bootstrap attribute), the "themeRoots" property of the window["sap-ui-config"] object must be used instead
* of Core.setThemeRoot(...) in order to configure the theme location early enough.
*
* @param {string} sThemeName the name of the theme for which to configure the location
* @param {string[]} [aLibraryNames] the optional library names to which the configuration should be restricted
* @param {string} sThemeBaseUrl the base URL below which the CSS file(s) will be loaded from
* @return {sap.ui.core.Core} the Core, to allow method chaining
* @since 1.10
* @public
*/
Core.prototype.setThemeRoot = function(sThemeName, aLibraryNames, sThemeBaseUrl) {
jQuery.sap.assert(typeof sThemeName === "string", "sThemeName must be a string");
jQuery.sap.assert((jQuery.isArray(aLibraryNames) && typeof sThemeBaseUrl === "string") || (typeof aLibraryNames === "string" && sThemeBaseUrl === undefined), "either the second parameter must be a string (and the third is undefined), or it must be an array and the third parameter is a string");
if (!this._mThemeRoots) {
this._mThemeRoots = {};
}
// normalize parameters
if (sThemeBaseUrl === undefined) {
sThemeBaseUrl = aLibraryNames;
aLibraryNames = undefined;
}
sThemeBaseUrl = sThemeBaseUrl + (sThemeBaseUrl.slice( -1) == "/" ? "" : "/");
if (aLibraryNames) {
// registration of URL for several libraries
for (var i = 0; i < aLibraryNames.length; i++) {
var lib = aLibraryNames[i];
this._mThemeRoots[sThemeName + " " + lib] = sThemeBaseUrl;
}
} else {
// registration of theme default base URL
this._mThemeRoots[sThemeName] = sThemeBaseUrl;
}
return this;
};
/**
* Initializes the Core after the initial page was loaded
* @private
*/
Core.prototype.init = function() {
if (this.bInitialized) {
return;
}
var log = jQuery.sap.log,
METHOD = "sap.ui.core.Core.init()";
// ensure that the core is booted now (e.g. loadAllMode)
this.boot();
log.info("Initializing",null,METHOD);
this.oFocusHandler = new FocusHandler(document.body, this);
this.oRenderManager._setFocusHandler(this.oFocusHandler); //Let the RenderManager know the FocusHandler
this.oResizeHandler = new ResizeHandler(this);
this.oThemeCheck = new ThemeCheck(this);
log.info("Initialized",null,METHOD);
this.bInitialized = true;
// start the plugins
log.info("Starting Plugins",null,METHOD);
this.startPlugins();
log.info("Plugins started",null,METHOD);
var oConfig = this.oConfiguration;
// create any pre-configured UIAreas
// if ( oConfig.areas && oConfig.areas.length > 0 ) {
if ( oConfig.areas ) {
// log.warning("deprecated config option '(data-sap-ui-)areas' used.");
for (var i = 0, l = oConfig.areas.length; i < l; i++) {
this.createUIArea(oConfig.areas[i]);
}
oConfig.areas = undefined;
}
// execute a configured init hook
if ( oConfig.onInit ) {
if ( typeof oConfig.onInit === "function" ) {
oConfig.onInit();
} else {
// DO NOT USE jQuery.globalEval as it executes async in FF!
jQuery.sap.globalEval(oConfig.onInit);
}
oConfig.onInit = undefined;
}
this.oThemeCheck.fireThemeChangedEvent(true);
// load the root component
var sRootComponent = oConfig.getRootComponent();
if (sRootComponent) {
log.info("Loading Root Component: " + sRootComponent,null,METHOD);
var oComponent = sap.ui.component({
name: sRootComponent
});
this.oRootComponent = oComponent;
var sRootNode = oConfig["xx-rootComponentNode"];
if (sRootNode && oComponent instanceof sap.ui.core.UIComponent) {
var oRootNode = jQuery.sap.domById(sRootNode);
if (oRootNode) {
log.info("Creating ComponentContainer for Root Component: " + sRootComponent,null,METHOD);
var oContainer = new sap.ui.core.ComponentContainer({
component: oComponent,
propagateModel: true /* TODO: is this a configuration or do this by default? right now it behaves like the application */
});
oContainer.placeAt(oRootNode);
}
}
} else {
// DEPRECATED LEGACY CODE: load the application (TODO: remove when Application is removed!)
var sApplication = oConfig.getApplication();
if (sApplication) {
log.warning("The configuration 'application' is deprecated. Please use the configuration 'component' instead! Please migrate from sap.ui.app.Application to sap.ui.core.Component.");
log.info("Loading Application: " + sApplication,null,METHOD);
jQuery.sap.require(sApplication);
var oClass = jQuery.sap.getObject(sApplication);
jQuery.sap.assert(oClass !== undefined, "The specified application \"" + sApplication + "\" could not be found!");
var oApplication = new oClass();
jQuery.sap.assert(oApplication instanceof sap.ui.app.Application, "The specified application \"" + sApplication + "\" must be an instance of sap.ui.app.Application!");
}
}
//Add ARIA role 'application'
var $body = jQuery("body");
if (oConfig.getAccessibility() && oConfig.getAutoAriaBodyRole() && !$body.attr("role")) {
$body.attr("role", "application");
}
// make sure that we have no concurrent modifications on the init listeners
var aCallbacks = this.aInitListeners;
// reset the init listener so that we are aware the listeners are already
// executed and the initialization phase is over / follow up registration
// would then immediately call the init event handler
this.aInitListeners = undefined;
// execute registered init event handlers
if (aCallbacks && aCallbacks.length > 0) {
// execute the callbacks
log.info("Fire Loaded Event",null,METHOD);
jQuery.each(aCallbacks, function(i,f) {
f();
});
}
this.renderPendingUIUpdates(); // directly render without setTimeout, so rendering is guaranteed to be finished when init() ends
};
/**
* Handles the load event of the browser to initialize the Core
* @private
*/
Core.prototype.handleLoad = function () {
this.bDomReady = true;
//do not allow any event processing until the Core is initialized
var bWasLocked = this.isLocked();
if ( !bWasLocked ) {
this.lock();
}
this.init();
//allow event processing again
if ( !bWasLocked ) {
this.unlock();
}
};
/**
* Returns true if the Core has already been initialized. This means that instances
* of RenderManager etc. do already exist and the init event has already been fired
* (and will not be fired again).
*
* @return {boolean} whether the Core has already been initialized
* @public
*/
Core.prototype.isInitialized = function () {
return this.bInitialized;
};
/**
* Returns true, if the styles of the current theme are already applied, false otherwise.
*
* This function must not be used before the init event of the Core.
* If the styles are not yet applied an theme changed event will follow when the styles will be applied.
*
* @return {boolean} whether the styles of the current theme are already applied
* @public
*/
Core.prototype.isThemeApplied = function () {
return ThemeCheck.themeLoaded;
};
/**
* Attaches a given function to the <code>initEvent</code> event of the core.
*
* This event will only be fired once; you can check if it has been fired already
* by calling {@link #isInitialized}.
*
* @param {function} fnFunction the function to be called on event firing.
* @public
* @deprecated since 1.13.2 Register to the more convenient {@link sap.ui.core.Core#attachInit init event} instead
*/
Core.prototype.attachInitEvent = function (fnFunction) {
jQuery.sap.assert(typeof fnFunction === "function", "fnFunction must be a function");
if (this.aInitListeners) {
this.aInitListeners.push(fnFunction);
}
};
/**
* Attaches a given function to the <code>initEvent</code> event of the core.
*
* The given callback function will either be called once the Core has been initialized
* or, if it has been initialized already, it will be called immediately.
*
* @param {function} fnFunction the callback function to be called on event firing.
* @public
* @since 1.13.2
*/
Core.prototype.attachInit = function (fnFunction) {
jQuery.sap.assert(typeof fnFunction === "function", "fnFunction must be a function");
if (this.aInitListeners) {
this.aInitListeners.push(fnFunction);
} else {
fnFunction();
}
};
/**
* Locks the Core. No browser events are dispatched to the controls.
*
* Lock should be called before and after the dom is modified for rendering, roundtrips...
* Exceptions might be the case for asynchronous UI behavior
* @public
*/
Core.prototype.lock = function () {
// TODO clarify it the documentation is really (stil?) true
this.bLocked = true;
};
/**
* Unlocks the Core.
*
* Browser events are dispatched to the controls again after this method is called.
* @public
*/
Core.prototype.unlock = function () {
this.bLocked = false;
};
/**
* Returns the locked state of the <code>sap.ui.core.Core</code>
* @return {boolean} locked state
* @public
*/
Core.prototype.isLocked = function () {
return this.bLocked;
};
/**
* Returns the Configuration of the Core.
*
* @return {sap.ui.core.Configuration} the Configuration of the current Core.
* @public
*/
Core.prototype.getConfiguration = function () {
return this.oConfiguration;
};
/**
* @public
* @deprecated Since version 0.15.0. Replaced by <code>createRenderManager()</code>
*/
Core.prototype.getRenderManager = function() {
return this.createRenderManager(); //this.oRenderManager;
};
/**
* Returns a new instance of the RenderManager interface.
*
* @return {sap.ui.core.RenderManager} the new instance of the RenderManager interface.
* @public
*/
Core.prototype.createRenderManager = function() {
var oRm = new RenderManager();
oRm._setFocusHandler(this.oFocusHandler); //Let the RenderManager know the FocusHandler
return oRm.getInterface();
};
/**
* Returns the Id of the control/element currently in focus.
* @return {string} the Id of the control/element currently in focus.
* @public
*/
Core.prototype.getCurrentFocusedControlId = function() {
if (!this.isInitialized()) {
throw new Error("Core must be initialized");
}
return this.oFocusHandler.getCurrentFocusedControlId();
};
/**
* Synchronously loads the given library and makes it available to the application.
*
* Loads the *.library module, which contains all preload modules (enums, types, content of a shared.js
* if it exists). The library module will call initLibrary with additional metadata for the library.
*
* As a result, consuming applications can instantiate any control or element from that library
* without having to write import statements for the controls or for the enums.
*
* When the optional parameter <code>sUrl</code> is given, then all request for resources of the
* library will be redirected to the given Url. This is convenience for a call to
* <pre>
* jQuery.sap.registerModulePath(sLibrary, sUrl);
* </pre>
*
* When the given library has been loaded already, no further action will be taken.
* Especially, a given Url will not be honored!
*
* Note: this method does not participate in the supported preload of libraries.
*
* @param {string} sLibrary name of the library to import
* @param {string} [sUrl] URL to load the library from
* @public
*/
Core.prototype.loadLibrary = function(sLibrary, sUrl) {
jQuery.sap.assert(typeof sLibrary === "string", "sLibrary must be a string");
jQuery.sap.assert(sUrl === undefined || typeof sUrl === "string", "sUrl must be a string or empty");
// load libraries only once
if ( !mLoadedLibraries[sLibrary] ) {
var sModule = sLibrary + ".library",
sAllInOneModule;
// if a sUrl is given, redirect access to it
if ( sUrl ) {
jQuery.sap.registerModulePath(sLibrary, sUrl);
}
// optimization: in all-in-one mode we are loading all modules of the lib in a single file
if ( this.oConfiguration['xx-loadAllMode'] && !jQuery.sap.isDeclared(sModule) ) {
sAllInOneModule = sModule + "-all";
jQuery.sap.log.debug("load all-in-one file " + sAllInOneModule);
jQuery.sap.require(sAllInOneModule);
} else if ( this.oConfiguration.preload === 'sync' || this.oConfiguration.preload === 'async' ) {
jQuery.sap.preloadModules(sModule + "-preload", /* force sync */ false);
}
// require the library module (which in turn will call initLibrary())
jQuery.sap.require(sModule);
// check for legacy code
if ( !mLoadedLibraries[sLibrary] ) {
jQuery.sap.log.warning("library " + sLibrary + " didn't initialize itself");
this.initLibrary(sLibrary); // TODO redundant to generated initLibrary call....
}
if ( this.oThemeCheck && this.isInitialized() ) {
this.oThemeCheck.fireThemeChangedEvent(true);
}
}
// Note: return parameter is undocumented by intention! Structure of lib info might change
return this.mLibraries[sLibrary];
};
/**
* Loads a set of libraries, preferably asynchronously.
*
* The module loading is still synchronous, so if a library loads additional modules besides
* its library.js file, those modules might be loaded synchronously by the library.js
* The async loading is only supported by the means of the library-preload.json files, so if a
* library doesn't provide a preload or when the preload is deactivated (configuration, debug mode)
* then this API falls back to synchronous loading. However, the contract (Promise) remains valid
* and a Promise will be returned if async is specified - even when the real loading
* is done synchronously.
*
* @param {string[]} aLibraries set of libraries that should be loaded
* @param {object} [mOptions] configuration options
* @param {boolean} [mOptions.async=true] whether to load the libraries async (default)
* @returns {Promise|undefined} returns an Ecmascript 6 promise for async, otherwise <code>undefined</code>
*
* @experimental Since 1.27.0 This API is not mature yet and might be changed or removed completely.
* Productive code should not use it, except code that is delivered as part of UI5.
* @private
*/
Core.prototype.loadLibraries = function(aLibraries, mOptions) {
mOptions = jQuery.extend({ async : true }, mOptions);
var that = this,
bPreload = this.oConfiguration.preload === 'sync' || this.oConfiguration.preload === 'async',
bAsync = mOptions.async;
function preloadLibs(oSyncPoint) {
if ( bPreload ) {
jQuery.each(aLibraries, function(i,sLibraryName) {
jQuery.sap.preloadModules(sLibraryName + ".library-preload", !!oSyncPoint, oSyncPoint);
});
}
}
function requireLibs() {
jQuery.each(aLibraries, function(i,sLibraryName) {
jQuery.sap.require(sLibraryName + ".library");
});
if ( that.oThemeCheck && that.isInitialized() ) {
that.oThemeCheck.fireThemeChangedEvent(true);
}
}
if ( bAsync && bPreload ) {
return new Promise(function(resolve, reject) {
// TODO we urgently need to get rid of our syncPoints, but jQuery.sap.preloadModules still uses them
var oSyncPoint = jQuery.sap.syncPoint("Load Libraries", function(iOpenTasks, iFailures) {
if ( !iFailures ) {
requireLibs();
resolve();
} else {
reject();
}
});
// create an artifical task to trigger the callback if no other tasks have been created
var iTask = oSyncPoint.startTask("load libraries");
preloadLibs(oSyncPoint);
oSyncPoint.finishTask(iTask);
});
} else {
preloadLibs(null);
requireLibs();
}
};
/**
* Creates a component with the provided id and settings.
*
* When the optional parameter <code>sUrl</code> is given, then all request for resources of the
* library will be redirected to the given Url. This is convenience for a call to
* <pre>
* jQuery.sap.registerModulePath(sName, sUrl);
* </pre>
*
* @param {string|object} vComponent name of the component to import or object containing all needed parameters
* @param {string} [vComponent.name] name of the component to import
* @param {string} [vComponent.url] URL to load the component from
* @param {string} [vComponent.id] ID for the component instance
* @param {object} [vComponent.settings] settings object for the component
* @param {string} [vComponent.componentData] user specific data which is available during the whole lifecycle of the component
* @param {string} [sUrl] the URL to load the component from
* @param {string} [sId] the ID for the component instance
* @param {object} [mSettings] the settings object for the component
* @public
*/
Core.prototype.createComponent = function(vComponent, sUrl, sId, mSettings) {
// convert the parameters into a configuration object
if (typeof vComponent === "string") {
vComponent = {
name: vComponent,
url: sUrl
};
// parameter fallback (analog to ManagedObject)
if (typeof sId === "object") {
vComponent.settings = sId;
} else {
vComponent.id = sId;
vComponent.settings = mSettings;
}
}
// use the factory function
return sap.ui.component(vComponent);
};
/**
* Returns the instance of the root component (if exists).
*
* @return {sap.ui.core.Component} instance of the current root component
* @public
*/
Core.prototype.getRootComponent = function() {
return this.oRootComponent;
};
/**
* Initializes a library for an already loaded library module.
*
* This method is intended to be called only from a library.js (e.g. generated code).
* It includes the library specific stylesheet into the current page, and creates
* lazy import stubs for all controls and elements in the library.
*
* As a result, consuming applications don't have to write import statements for the controls or for the enums.
*
* Synchronously loads any libraries that the given library depends on.
*
* @param {string|object} vLibInfo name of or info object for the library to import
* @public
*/
Core.prototype.initLibrary = function(vLibInfo) {
jQuery.sap.assert(typeof vLibInfo === "string" || typeof vLibInfo === "object", "vLibInfo must be a string or object");
var bLegacyMode = typeof vLibInfo === "string",
oLibInfo = bLegacyMode ? { name : vLibInfo } : vLibInfo,
sLibName = oLibInfo.name,
log = jQuery.sap.log,
METHOD = "sap.ui.core.Core.initLibrary()";
if ( bLegacyMode ) {
log.warning("[Deprecated] library " + sLibName + " uses old fashioned initLibrary() call (rebuild with newest generator)");
}
if ( !sLibName || mLoadedLibraries[sLibName] ) {
return;
}
log.debug("Analyzing Library " + sLibName, null, METHOD);
// Set 'loaded' marker
mLoadedLibraries[sLibName] = true;
function extend(oLibrary, oInfo) {
var sKey, vValue;
for ( sKey in oInfo ) {
vValue = oInfo[sKey];
// don't set name again, don't copy undefined values
if ( vValue !== undefined ) {
if ( jQuery.isArray(oLibrary[sKey]) ) {
// concat array typed values
if ( oLibrary[sKey].length === 0 ) {
oLibrary[sKey] = vValue;
} else {
oLibrary[sKey] = jQuery.sap.unique(oLibrary[sKey].concat(vValue));
}
} else if ( oLibrary[sKey] === undefined ) {
// only set values for properties that are still undefined
oLibrary[sKey] = vValue;
} else {
// ignore other values
jQuery.sap.log.warning("library info setting ignored: " + sKey + "=" + vValue);
}
}
}
return oLibrary;
}
// Create lib info object or merge with existing 'adhoc' library
this.mLibraries[sLibName] = oLibInfo = extend(this.mLibraries[sLibName] || {
name : sLibName,
dependencies : [],
types : [],
interfaces : [],
controls: [],
elements : []
}, oLibInfo);
// this code could be moved to a separate "legacy support" module
function readLibInfoFromProperties() {
// read library properties
var oProperties = jQuery.sap.properties({url : sap.ui.resource(sLibName, "library.properties")});
// version info
oLibInfo.version = oProperties.getProperty(sLibName + "[version]");
// dependencies
var sDepInfo = oProperties.getProperty(sLibName + "[dependencies]");
log.debug("Required Libraries: " + sDepInfo, null, METHOD);
oLibInfo.dependencies = (sDepInfo && sDepInfo.split(/[,;| ]/)) || [];
// collect types, controls and elements
var aKeys = oProperties.getKeys(),
rPattern = /(.+)\.(type|interface|control|element)$/,
aMatch;
for (var j = 0; j < aKeys.length; j++) {
var sEntityPath = oProperties.getProperty(aKeys[j]);
if ( (aMatch = sEntityPath.match(rPattern)) !== null ) {
oLibInfo[aMatch[2] + "s"].push(aKeys[j]);
}
}
}
// (legacy) if only a string was given, read the library.properties instead
if ( bLegacyMode ) {
readLibInfoFromProperties();
}
// resolve dependencies
for (var i = 0; i < oLibInfo.dependencies.length; i++) {
var sDepLib = oLibInfo.dependencies[i];
log.debug("resolve Dependencies to " + sDepLib, null, METHOD);
if ( !mLoadedLibraries[sDepLib] ) {
log.warning("Dependency from " + sLibName + " to " + sDepLib + " has not been resolved by library itself", null, METHOD);
this.loadLibrary(sDepLib);
}
}
// register interface types
DataType.registerInterfaceTypes(oLibInfo.interfaces);
// Declare a module for each (non-builtin) simple type
// Only needed for backward compatibility: some code 'requires' such types although they never have been modules on their own
for (var i = 0; i < oLibInfo.types.length; i++) {
if ( !/^(any|boolean|float|int|string|object|void)$/.test(oLibInfo.types[i]) ) {
jQuery.sap.declare(oLibInfo.types[i]);
}
}
// create lazy imports for all controls and elements
var aElements = oLibInfo.controls.concat(oLibInfo.elements);
for (var i = 0; i < aElements.length; i++) {
sap.ui.lazyRequire(aElements[i], "new extend getMetadata"); // TODO don't create an 'extend' stub for final classes
}
// include the library theme, but only if it has not been suppressed in library metadata or by configuration
if ( !oLibInfo.noLibraryCSS && jQuery.inArray(sLibName, this.oConfiguration['preloadLibCss']) < 0 ) {
this.includeLibraryTheme(sLibName);
}
// expose some legacy names
oLibInfo.sName = oLibInfo.name;
oLibInfo.aControls = oLibInfo.controls;
// load and execute the library.js script
if ( !jQuery.sap.isDeclared(sLibName + ".library") ) {
// TODO redundant to generated require calls
log.warning("Library Module " + sLibName + ".library" + " not loaded automatically", null, METHOD);
jQuery.sap.require(sLibName + ".library");
}
this.fireLibraryChanged({name : sLibName, stereotype : "library", operation: "add", metadata : oLibInfo});
};
/**
* Includes a library theme into the current page (if a variant is specified it
* will include the variant library theme)
* @param {string} sLibName the name of the UI library
* @param {string} [sVariant] the variant to include (optional)
* @param {string} [sQuery] to be used only by the Core
* @public
*/
Core.prototype.includeLibraryTheme = function(sLibName, sVariant, sQuery) {
jQuery.sap.assert(typeof sLibName === "string", "sLibName must be a string");
jQuery.sap.assert(sVariant === undefined || typeof sVariant === "string", "sVariant must be a string or undefined");
/*
* by specifiying a library name containing a colon (":") you can specify
* the file name of the CSS file to include (ignoring RTL)
*/
// include the stylesheet for the library (except for "classic" and "legacy" lib)
if ((sLibName != "sap.ui.legacy") && (sLibName != "sap.ui.classic")) {
// no variant?
if (!sVariant) {
sVariant = "";
}
// determine RTL
var sRtl = (this.oConfiguration.getRTL() ? "-RTL" : "");
// create the library file name
var sLibFileName,
sLibId = sLibName + (sVariant.length > 0 ? "-[" + sVariant + "]" : sVariant);
if (sLibName && sLibName.indexOf(":") == -1) {
sLibFileName = "library" + sVariant + sRtl;
} else {
sLibFileName = sLibName.substring(sLibName.indexOf(":") + 1) + sVariant;
sLibName = sLibName.substring(0, sLibName.indexOf(":"));
}
// log and include
var cssPathAndName = this._getThemePath(sLibName, this.sTheme) + sLibFileName + ".css" + (sQuery ? sQuery : "");
jQuery.sap.log.info("Including " + cssPathAndName + " - sap.ui.core.Core.includeLibraryTheme()");
jQuery.sap.includeStyleSheet(cssPathAndName, "sap-ui-theme-" + sLibId);
// if parameters have been used, update them with the new style sheet
if (sap.ui.core.theming && sap.ui.core.theming.Parameters) {
sap.ui.core.theming.Parameters._addLibraryTheme(sLibId, cssPathAndName);
}
}
};
/**
* Returns a map which contains the names of the loaded libraries as keys
* and some additional information about each library as values.
*
* @experimental The details of the 'values' in the returned map are not yet specified!
* Their structure might change in future versions without notice. So applications
* can only rely on the set of keys as well as the pure existance of a value.
*
* @return {map} map of library names / controls
* @public
*/
Core.prototype.getLoadedLibraries = function() {
return jQuery.extend({}, this.mLibraries); // TODO deep copy or real Library object?
};
/**
* Retrieves a resource bundle for the given library and locale.
*
* If only one argument is given, it is assumed to be the libraryName. The locale
* then falls back to the current {@link sap.ui.core.Configuration.prototype.getLanguage session locale}.
* If no argument is given, the library also falls back to a default: "sap.ui.core".
*
* @param {string} [sLibraryName='sap.ui.core'] name of the library to retrieve the bundle for
* @param {string} [sLocale] locale to retrieve the resource bundle for
* @return {jQuery.sap.util.ResourceBundle} the best matching resource bundle for the given parameters or undefined
* @public
*/
Core.prototype.getLibraryResourceBundle = function(sLibraryName, sLocale) {
jQuery.sap.assert((sLibraryName === undefined && sLocale === undefined) || typeof sLibraryName === "string", "sLibraryName must be a string or there is no argument given at all");
jQuery.sap.assert(sLocale === undefined || typeof sLocale === "string", "sLocale must be a string or omitted");
// TODO move implementation together with similar stuff to a new class "UILibrary"?
sLibraryName = sLibraryName || "sap.ui.core";
sLocale = sLocale || this.getConfiguration().getLanguage();
var sKey = sLibraryName + "/" + sLocale;
if (!this.mResourceBundles[sKey]) {
var sURL = sap.ui.resource(sLibraryName, 'messagebundle.properties');
this.mResourceBundles[sKey] = jQuery.sap.resources({url : sURL, locale : sLocale});
}
return this.mResourceBundles[sKey];
};
// ---- UIArea and Rendering -------------------------------------------------------------------------------------
/**
* Implicitly creates a new <code>UIArea</code> (or reuses an exiting one) for the given DOM reference and
* adds the given control reference to the UIAreas content (existing content will be removed).
*
* @param {string|Element} oDomRef a DOM Element or Id (string) of the UIArea
* @param {sap.ui.base.Interface | sap.ui.core.Control}
* oControl the Control that should be the added to the <code>UIArea</code>.
* @public
* @deprecated Use function <code>oControl.placeAt(oDomRef, "only")</code> of <code>sap.ui.core.Control</code> instead.
*/
Core.prototype.setRoot = function(oDomRef, oControl) {
jQuery.sap.assert(typeof oDomRef === "string" || typeof oDomRef === "object", "oDomRef must be a string or object");
jQuery.sap.assert(oControl instanceof sap.ui.base.Interface || oControl instanceof Control, "oControl must be a Control or Interface");
if (oControl) {
oControl.placeAt(oDomRef, "only");
}
};
/**
* Creates a new sap.ui.core.UIArea.
*
* @param {string|Element} oDomRef a DOM Element or ID string of the UIArea
* @return {sap.ui.core.UIArea} a new UIArea
* @public
* @deprecated Use <code>setRoot()</code> instead!
*/
Core.prototype.createUIArea = function(oDomRef) {
var that = this;
jQuery.sap.assert(typeof oDomRef === "string" || typeof oDomRef === "object", "oDomRef must be a string or object");
if (!oDomRef) {
throw new Error("oDomRef must not be null");
}
// oDomRef might be (and actually IS in most cases!) a string (the ID of a DOM element)
if (typeof (oDomRef) === "string") {
var id = oDomRef;
if (id == STATIC_UIAREA_ID) {
oDomRef = this.getStaticAreaRef();
} else {
oDomRef = jQuery.sap.domById(oDomRef);
if (!oDomRef) {
throw new Error("DOM element with ID '" + id + "' not found in page, but application tries to insert content.");
}
}
}
// if the domref does not have an ID or empty ID => generate one
if (!oDomRef.id || oDomRef.id.length == 0) {
oDomRef.id = jQuery.sap.uid();
}
// create a new or fetch an existing UIArea
var sId = oDomRef.id;
if (!this.mUIAreas[sId]) {
this.mUIAreas[sId] = new UIArea(this, oDomRef);
// propagate Models to newly created UIArea
jQuery.each(this.oModels, function (sName, oModel){
that.mUIAreas[sId].oPropagatedProperties.oModels[sName] = oModel;
});
this.mUIAreas[sId].propagateProperties(true);
} else {
// this should solve the issue of 'recreation' of a UIArea
// e.g. via setRoot with a new domRef
this.mUIAreas[sId].setRootNode(oDomRef);
}
return this.mUIAreas[sId];
};
/**
* Returns a UIArea if the given ID/Element belongs to one.
*
* @public
* @param {string|Element} o a DOM Element or ID string of the UIArea
* @return {sap.ui.core.UIArea} a UIArea with a given id or dom ref.
*/
Core.prototype.getUIArea = function(o) {
jQuery.sap.assert(typeof o === "string" || typeof o === "object", "o must be a string or object");
var sId = "";
if (typeof (o) == "string") {
sId = o;
} else {
sId = o.id;
}
if (sId) {
return this.mUIAreas[sId];
}
return null;
};
// share the rendering log with the UIArea
var oRenderLog = UIArea._oRenderLog;
/**
* Informs the core about an UIArea that just became invalid.
*
* The core might use this information to minimize the set of
* re-rendered UIAreas. But for the time being it just registers
* a timer to trigger a re-rendering after the current event
* has been processed.
*
* @param {sap.ui.core.UIArea} oUIArea UIArea that just became invalid
* @private
*/
Core.prototype.addInvalidatedUIArea = function(oUIArea) {
if ( !this._sRerenderTimer ) {
oRenderLog.debug("Registering timer for delayed re-rendering");
this._sRerenderTimer = jQuery.sap.delayedCall(0,this,"renderPendingUIUpdates"); // decoupled for collecting several invalidations into one redraw
}
};
Core.MAX_RENDERING_ITERATIONS = 20;
/**
* Asks all UIAreas to execute any pending rendering tasks.
*
* The execution of rendering tasks might require multiple iterations
* until either no more rendering tasks are produced or until
* MAX_RENDERING_ITERATIONS are reached.
*
* With a value of MAX_RENDERING_ITERATIONS=0 the loop can be avoided
* and the remaining tasks are executed after another timeout.
*
* @private
*/
Core.prototype.renderPendingUIUpdates = function() {
// start performance measurement
oRenderLog.debug("Render pending UI updates: start");
jQuery.sap.measure.start("renderPendingUIUpdates","Render pending UI updates in all UIAreas");
var bUIUpdated = false,
bLooped = Core.MAX_RENDERING_ITERATIONS > 0,
iLoopCount = 0;
this._bRendering = true;
do {
if ( bLooped ) {
// try to detect long running ('endless') rendering loops
iLoopCount++;
// if we run another iteration despite the tracking mode, we complain ourselves
if ( iLoopCount > Core.MAX_RENDERING_ITERATIONS ) {
this._bRendering = false;
throw new Error("Rendering has been re-started too many times (" + iLoopCount + "). Add URL parameter sap-ui-xx-debugRendering=true for a detailed analysis.");
}
if ( iLoopCount > 1 ) {
oRenderLog.debug("Render pending UI updates: iteration " + iLoopCount);
}
}
// clear a pending timer so that the next call to re-render will create a new timer
if (this._sRerenderTimer) {
jQuery.sap.clearDelayedCall(this._sRerenderTimer); // explicitly stop the timer, as this call might be a synchronous call (applyChanges) while still a timer is running
this._sRerenderTimer = undefined;
}
// avoid 'concurrent modifications' as IE8 can't handle them
var mUIAreas = this.mUIAreas;
for (var sId in mUIAreas) {
bUIUpdated = mUIAreas[sId].rerender() || bUIUpdated;
}
} while ( bLooped && this._sRerenderTimer ); // iterate if there are new rendering tasks
this._bRendering = false;
// TODO: Provide information on what actually was re-rendered...
if (bUIUpdated) {
this.fireUIUpdated();
}
oRenderLog.debug("Render pending UI updates: finished");
// end performance measurement
jQuery.sap.measure.end("renderPendingUIUpdates");
};
/**
* Returns <code>true</code> if there are any pending rendering tasks or when
* such rendering tasks are currently being executed.
*
* @return {boolean} true if there are pending (or executing) rendering tasks.
* @public
*/
Core.prototype.getUIDirty = function() {
return !!(this._sRerenderTimer || this._bRendering);
};
/**
* @name sap.ui.core.Core#UIUpdated
* @event
* @private
* @function
*/
Core.prototype.attachUIUpdated = function(fnFunction, oListener) {
this.attachEvent(Core.M_EVENTS.UIUpdated, fnFunction, oListener);
};
Core.prototype.detachUIUpdated = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.UIUpdated, fnFunction, oListener);
};
Core.prototype.fireUIUpdated = function(mParameters) {
this.fireEvent(Core.M_EVENTS.UIUpdated, mParameters);
};
/**
* @name sap.ui.core.Core#ThemeChanged
* @event
* @param {string} theme name of the new theme
* @function
*/
Core.prototype.attachThemeChanged = function(fnFunction, oListener) {
this.attachEvent(Core.M_EVENTS.ThemeChanged, fnFunction, oListener);
};
Core.prototype.detachThemeChanged = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.ThemeChanged, fnFunction, oListener);
};
Core.prototype.fireThemeChanged = function(mParameters) {
jQuery.sap.scrollbarSize(true);
// special hook for resetting theming parameters before the controls get
// notified (lightweight coupling to static Parameters module)
if (sap.ui.core.theming && sap.ui.core.theming.Parameters) {
sap.ui.core.theming.Parameters.reset(/* bOnlyWhenNecessary= */ true);
}
// notify all elements/controls via a pseudo browser event
var sEventId = Core.M_EVENTS.ThemeChanged;
var oEvent = jQuery.Event(sEventId);
oEvent.theme = mParameters ? mParameters.theme : null;
jQuery.each(this.mElements, function(sId, oElement) {
oElement._handleEvent(oEvent);
});
jQuery.sap.act.refresh();
// notify the listeners via a control event
this.fireEvent(sEventId, mParameters);
};
/**
* Fired when any of the localization relevant configuration settings has changed
* (e.g. language, rtl, formatLocale, datePattern, timePattern, numberSymbol, legacy formats).
*
* The parameter <code>changes</code> contains additional information about the change.
* It is a plain object that can contain one or more of the following properties
* <ul>
* <li>language - the language setting has changed</li>
* <li>rtl - the character orientation mode (aka 'LTR/RTL mode') has changed</li>
* <li>formatLocale - the format locale has changed</li>
* </ul>
* (there might be other, currently undocumented settings)
*
* The value for each property will be the new corresponding setting.
*
* @name sap.ui.core.Core#localizationChanged
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {object} oEvent.getParameters.changes a map of the changed localization properties
* @public
*/
/**
* Register a listener for the <code>localizationChanged</code> event.
*
* @param {function} fnFunction callback to be called
* @param {object} oListener context object to cal lthe function on.
* @public
*/
Core.prototype.attachLocalizationChanged = function(fnFunction, oListener) {
this.attachEvent(Core.M_EVENTS.LocalizationChanged, fnFunction, oListener);
};
/**
* Unregister a listener from the <code>localizationChanged</code> event.
*
* The listener will only be unregistered if the same function/context combination
* is given as in the call to <code>attachLocalizationListener</code>.
*
* @param {function} fnFunction callback to be deregistered
* @param {object} oListener context object given in a previous call to attachLocalizationChanged.
* @public
*/
Core.prototype.detachLocalizationChanged = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.LocalizationChanged, fnFunction, oListener);
};
/**
* @private
*/
Core.prototype.fireLocalizationChanged = function(mChanges) {
var sEventId = Core.M_EVENTS.LocalizationChanged,
oBrowserEvent = jQuery.Event(sEventId, {changes : mChanges}),
fnAdapt = sap.ui.base.ManagedObject._handleLocalizationChange,
changedSettings = [];
jQuery.each(mChanges, function(key,value) {
changedSettings.push(key);
});
jQuery.sap.log.info("localization settings changed: " + changedSettings.join(","), null, "sap.ui.core.Core");
/*
* Notify models that are able to handle a localization change
*/
jQuery.each(this.oModels, function(sName, oModel) {
if ( oModel && oModel._handleLocalizationChange ) {
oModel._handleLocalizationChange();
}
});
/*
* Notify all UIAreas, Components, Elements to first update their models (phase 1)
* and then to update their bindings and corresponding data types (phase 2)
*/
function notifyAll(iPhase) {
jQuery.each(this.mUIAreas, function() {
fnAdapt.call(this, iPhase);
});
jQuery.each(this.mObjects["component"], function() {
fnAdapt.call(this, iPhase);
});
jQuery.each(this.mElements, function() {
fnAdapt.call(this, iPhase);
});
}
notifyAll.call(this,1);
notifyAll.call(this,2);
// special handling for changes of the RTL mode
if ( mChanges.rtl != undefined ) {
// update the dir attribute of the document
jQuery(document.documentElement).attr("dir", mChanges.rtl ? "rtl" : "ltr");
// modify style sheet URLs
this._updateThemeUrls(this.sTheme);
// invalidate all UIAreas
jQuery.each(this.mUIAreas, function() {
this.invalidate();
});
jQuery.sap.log.info("RTL mode " + mChanges.rtl ? "activated" : "deactivated");
}
// notify Elements via a pseudo browser event (onLocalizationChanged)
jQuery.each(this.mElements, function(sId, oElement) {
this._handleEvent(oBrowserEvent);
});
// notify registered Core listeners
this.fireEvent(sEventId, {changes : mChanges});
};
/**
* Fired when the set of controls, elements etc. for a library has changed
* or when the set of libraries has changed.
*
* Note: while the parameters of this event could already describe <i>any</i> type of change,
* the set of reported changes is currently restricted to the addition of libraries,
* controls and elements. Future implementations might extend the set of reported
* changes. Therefore applications should already check the operation and stereotype
* parameters.
*
* @name sap.ui.core.Core#libraryChanged
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.name name of the newly added entity
* @param {string} [oEvent.getParameters.stereotype] stereotype of the newly added entity type ("control", "element")
* @param {string} [oEvent.getParameters.operation] type of operation ("add")
* @param {sap.ui.base.Metadata|object} [oEvent.getParameters.metadata] metadata for the added entity type.
* Either an instance of sap.ui.core.ElementMetadata if it is a Control or Element, or a library info object
* if it is a library. Note that the API of all metadata objects is not public yet and might change.
*/
/**
* Register a listener for the {@link sap.ui.core.Core#event:libraryChanged} event.
*/
Core.prototype.attachLibraryChanged = function(fnFunction, oListener) {
this.attachEvent(Core.M_EVENTS.LibraryChanged, fnFunction, oListener);
};
/**
* Unregister a listener from the {@link sap.ui.core.Core#event:libraryChanged} event.
*/
Core.prototype.detachLibraryChanged = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.LibraryChanged, fnFunction, oListener);
};
/**
* @private
*/
Core.prototype.fireLibraryChanged = function(oParams) {
// notify registered Core listeners
this.fireEvent(Core.M_EVENTS.LibraryChanged, oParams);
};
/**
* Enforces an immediate update of the visible UI (aka "rendering").
*
* In general, applications should avoid calling this method and
* instead let the framework manage any necessary rendering.
* @public
*/
Core.prototype.applyChanges = function() {
this.renderPendingUIUpdates();
};
/**
* @private
*/
Core.prototype.registerElementClass = function(oMetadata) {
var sName = oMetadata.getName(),
sLibraryName = oMetadata.getLibraryName() || "",
oLibrary = this.mLibraries[sLibraryName],
sCategory = Control.prototype.isPrototypeOf(oMetadata.getClass().prototype) ? 'controls' : 'elements';
// if library has not been loaded yet, create empty 'adhoc' library
// don't set 'loaded' marker, so it might be loaded later
if ( !oLibrary ) {
oLibrary = this.mLibraries[sLibraryName] = {
name: sLibraryName,
dependencies : [],
types : [],
interfaces : [],
controls: [],
elements : []
};
}
if ( jQuery.inArray(sName, oLibrary[sCategory]) < 0 ) {
// add class to corresponding category in library ('elements' or 'controls')
oLibrary[sCategory].push(sName);
jQuery.sap.log.debug("Class " + oMetadata.getName() + " registered for library " + oMetadata.getLibraryName());
this.fireLibraryChanged({name : oMetadata.getName(), stereotype : oMetadata.getStereotype(), operation: "add", metadata : oMetadata});
}
};
/**
* Registers the given element. Must be called once during construction.
* @param {sap.ui.core.Element} oElement
* @private
*/
Core.prototype.registerElement = function(oElement) {
var oldElement = this.byId(oElement.getId());
if ( oldElement && oldElement !== oElement ) {
// duplicate ID detected => fail or at least log a warning
if (this.oConfiguration.getNoDuplicateIds()) {
jQuery.sap.log.error("adding element with duplicate id '" + oElement.getId() + "'");
throw new Error("Error: adding element with duplicate id '" + oElement.getId() + "'");
} else {
jQuery.sap.log.warning("adding element with duplicate id '" + oElement.getId() + "'");
}
}
this.mElements[oElement.getId()] = oElement;
};
/**
* Deregisters the given element. Must be called once during destruction.
* @param {sap.ui.core.Element} oElement
* @private
*/
Core.prototype.deregisterElement = function(oElement) {
delete this.mElements[oElement.getId()];
};
/**
* Registers the given object. Must be called once during construction.
* @param {sap.ui.core.ManagedObject} oObject the object instance
* @private
*/
Core.prototype.registerObject = function(oObject) {
var sId = oObject.getId(),
sType = oObject.getMetadata().getStereotype(),
oldObject = this.getObject(sType, sId);
if ( oldObject && oldObject !== oObject ) {
jQuery.sap.log.error("adding object \"" + sType + "\" with duplicate id '" + sId + "'");
throw new Error("Error: adding object \"" + sType + "\" with duplicate id '" + sId + "'");
}
this.mObjects[sType][sId] = oObject;
};
/**
* Deregisters the given object. Must be called once during destruction.
* @param {sap.ui.core.ManagedObject} oObject the object instance
* @private
*/
Core.prototype.deregisterObject = function(oObject) {
var sId = oObject.getId(),
sType = oObject.getMetadata().getStereotype();
delete this.mObjects[sType][sId];
};
/**
* Returns the registered element for the given id, if any.
* @param {string} sId
* @return {sap.ui.core.Element} the element for the given id
* @public
*/
Core.prototype.byId = function(sId) {
jQuery.sap.assert(sId == null || typeof sId === "string", "sId must be a string when defined");
// allow null, as this occurs frequently and it is easier to check whether there is a control in the end than
// first checking whether there is an ID and then checking for a control
/*
// test alternative implementation
function findById(sId, mUIAreas) {
function _find(oControl) {
if ( !oControl )
return undefined;
if ( oControl.getId() === sId ) {
return oControl;
}
for (var n in oControl.mAggregations) {
var a = oControl.mAggregations[n];
if ( jQuery.isArray(a) ) {
for (var i=0; i<a.length; i++) {
var r = _find(a[i]);
if ( r ) return r;
}
} else if ( a instanceof sap.ui.core.Element ) {
var r = _find(a[i]);
if ( r ) return r;
}
}
return undefined;
}
//var t0=new Date().getTime();
var r=undefined;
for (var n in mUIAreas) {
r=_find(mUIAreas[n].getRootControl()); //TODO: Adapt to mUIAreas[n].getContent
if ( r ) break;
}
//var t1=new Date().getTime();
//t=t+(t1-t0);
return r;
}
if ( findById(sId, this.mUIAreas) !== this.mElements[sId] ) {
jQuery.sap.log.error("failed to resolve " + sId + " (" + this.mElements[sId] + ")");
}
*/
return sId == null ? undefined : this.mElements[sId];
};
/**
* Returns the registered element for the given ID, if any.
* @param {string} sId
* @return {sap.ui.core.Element} the element for the given id
* @deprecated use <code>sap.ui.core.Core.byId</code> instead!
* @function
* @public
*/
Core.prototype.getControl = Core.prototype.byId;
/**
* Returns the registered element for the given ID, if any.
* @param {string} sId
* @return {sap.ui.core.Element} the element for the given id
* @deprecated use <code>sap.ui.core.Core.byId</code> instead!
* @function
* @public
*/
Core.prototype.getElementById = Core.prototype.byId;
/**
* Returns the registered object for the given id, if any.
* @param {string} sType
* @param {string} sId
* @return {sap.ui.core.Component} the component for the given id
* @private
*/
Core.prototype.getObject = function(sType, sId) {
jQuery.sap.assert(sId == null || typeof sId === "string", "sId must be a string when defined");
jQuery.sap.assert(this.mObjects[sType] !== undefined, "sType must be a supported stereotype");
return sId == null ? undefined : this.mObjects[sType] && this.mObjects[sType][sId];
};
/**
* Returns the registered component for the given id, if any.
* @param {string} sId
* @return {sap.ui.core.Component} the component for the given id
* @public
*/
Core.prototype.getComponent = function(sId) {
return this.getObject("component", sId);
};
/**
* Returns the registered template for the given id, if any.
* @param {string} sId
* @return {sap.ui.core.Component} the template for the given id
* @public
*/
Core.prototype.getTemplate = function(sId) {
return this.getObject("template", sId);
};
/**
* Returns the static, hidden area DOM element belonging to this core instance.
*
* It can be used e.g. for hiding elements like Popups, Shadow, Blocklayer etc.
*
* If it is not yet available, a DIV is created and appended to the body.
*
* @return {Element} the static, hidden area DOM element belonging to this core instance.
* @throws {Error} an Error if the document is not yet ready
* @public
*/
Core.prototype.getStaticAreaRef = function() {
var oStatic = jQuery.sap.domById(STATIC_UIAREA_ID);
if (!oStatic) {
if (!this.bDomReady) {
throw new Error("DOM is not ready yet. Static UIArea cannot be created.");
}
var oAttributes = {id:STATIC_UIAREA_ID};
if (jQuery("body").attr("role") != "application") {
// Only set ARIA application role if not available on html body (see configuration entry "autoAriaBodyRole")
oAttributes.role = "application";
}
var leftRight = this.getConfiguration().getRTL() ? "right" : "left";
oStatic = jQuery("<DIV/>", oAttributes).css({
"height" : "0",
"width" : "0",
"overflow" : "hidden",
"float" : leftRight
}).prependTo(document.body)[0];
// TODO Check whether this is sufficient
this.createUIArea(oStatic).bInitial = false;
}
return oStatic;
};
/**
* Used to find out whether a certain DOM element is the static area
*
* @param {object} oDomRef
* @return {boolean} whether the given DomRef is the StaticAreaRef
* @protected
*/
Core.prototype.isStaticAreaRef = function(oDomRef) {
return oDomRef && (oDomRef.id === STATIC_UIAREA_ID);
};
/**
* Interval for central interval timer.
* @private
*/
Core._I_INTERVAL = 200;
/**
* Obsolete but kept for backward compatibility.
* Note that the ResizeHandler has been required above, so we can access it here.
* @private
* @name sap.ui.core.ResizeHandler#I_INTERVAL
*/
ResizeHandler.prototype.I_INTERVAL = Core._I_INTERVAL;
/**
* Registers a listener to the central interval timer.
*
* @param {function} fnFunction callback to be called periodically
* @param {object} [oListener] optional context object to call the callback on.
* @since 1.16.0
* @public
*/
Core.prototype.attachIntervalTimer = function(fnFunction, oListener) {
if (!this.oTimedTrigger) {
jQuery.sap.require("sap.ui.core.IntervalTrigger");
this.oTimedTrigger = new sap.ui.core.IntervalTrigger(Core._I_INTERVAL);
}
this.oTimedTrigger.addListener(fnFunction, oListener);
};
/**
* Unregisters a listener for the central interval timer.
*
* A listener will only be unregistered if the same function/context combination
* is given as in the attachIntervalTimer call.
*
* @param {function} fnFunction function to unregister
* @param {object} [oListener] context object given during registration
* @since 1.16.0
* @public
*/
Core.prototype.detachIntervalTimer = function(fnFunction, oListener) {
if (this.oTimedTrigger) {
this.oTimedTrigger.removeListener(fnFunction, oListener);
}
};
/**
* Registers a listener for control events.
*
* @param {function} fnFunction callback to be called for each control event
* @param {object} [oListener] optional context object to call the callback on.
* @public
*/
Core.prototype.attachControlEvent = function(fnFunction, oListener) {
this.attachEvent(Core.M_EVENTS.ControlEvent, fnFunction, oListener);
};
/**
* Unregisters a listener for control events.
*
* A listener will only be unregistered if the same function/context combination
* is given as in the attachControlEvent call.
*
* @param {function} fnFunction function to unregister
* @param {object} [oListener] context object given during registration
* @public
*/
Core.prototype.detachControlEvent = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.ControlEvent, fnFunction, oListener);
};
/**
* Notifies the listeners that a event on a control occures
* @param {map} mParameters { browserEvent: jQuery.EventObject }
* @private
*/
Core.prototype.fireControlEvent = function(mParameters) {
this.fireEvent(Core.M_EVENTS.ControlEvent, mParameters);
};
/**
* Handles a control event and forwards it to the registered control event listeners.
*
* @param {jQuery.EventObject} oEvent control event
* @param {string} sUIAreaId id of the UIArea that received the event
* @private
*/
Core.prototype._handleControlEvent = function(/**event*/oEvent, sUiAreaId) {
// Create a copy of the event
var oEventClone = jQuery.Event(oEvent.type);
jQuery.extend(oEventClone, oEvent);
oEventClone.originalEvent = undefined;
this.fireControlEvent({"browserEvent": oEventClone, "uiArea": sUiAreaId});
};
/**
* Returns the instance of the application (if exists).
*
* @return {sap.ui.app.Application} instance of the current application
* @public
* @deprecated Since 1.15.1. The Component class is enhanced to take care about the Application code.
*/
Core.prototype.getApplication = function() {
return sap.ui.getApplication && sap.ui.getApplication();
};
/**
* Registers a Plugin to the <code>sap.ui.core.Core</code>, which lifecycle
* will be managed (start and stop).
* <br/>
* Plugin object need to implement two methods:
* <ul>
* <li><code>startPlugin(oCore)</code>: will be invoked, when the Plugin
* should start (as parameter the reference to the Core will be provided</li>
* <li><code>stopPlugin()</code>: will be invoked, when the Plugin should stop</li>
* </ul>
*
* @param {object} oPlugin reference to a Plugin object
* @public
*/
Core.prototype.registerPlugin = function(oPlugin) {
jQuery.sap.assert(typeof oPlugin === "object", "oPlugin must be an object");
// check for a valid plugin
if (!oPlugin) {
return;
}
// check if the plugin is already registered
// if yes, the exit this function
for (var i = 0, l = this.aPlugins.length; i < l; i++) {
if (this.aPlugins[i] === oPlugin) {
return;
}
}
// register the plugin (keep the plugin in the plugin array)
this.aPlugins.push(oPlugin);
// if the Core is initialized also start the plugin
if (this.bInitialized && oPlugin && oPlugin.startPlugin) {
oPlugin.startPlugin(this);
}
};
/**
* Unregisters a Plugin out of the <code>sap.ui.core.Core</code>
*
* @param {object} oPlugin reference to a Plugin object
* @public
*/
Core.prototype.unregisterPlugin = function(oPlugin) {
jQuery.sap.assert(typeof oPlugin === "object", "oPlugin must be an object");
// check for a valid plugin
if (!oPlugin) {
return;
}
// check if the plugin is already registered
var iPluginIndex = -1;
for (var i = this.aPlugins.length; i--; i >= 0) {
if (this.aPlugins[i] === oPlugin) {
iPluginIndex = i;
break;
}
}
// plugin was not registered!
if (iPluginIndex == -1) {
return;
}
// stop the plugin
if (this.bInitialized && oPlugin && oPlugin.stopPlugin) {
oPlugin.stopPlugin(this);
}
// remove the plugin
this.aPlugins.splice(iPluginIndex, 1);
};
/**
* Internal method to start all registered plugins
* @private
*/
Core.prototype.startPlugins = function() {
for (var i = 0, l = this.aPlugins.length; i < l; i++) {
var oPlugin = this.aPlugins[i];
if (oPlugin && oPlugin.startPlugin) {
oPlugin.startPlugin(this, /* onInit*/ true);
}
}
};
/**
* Internal method to stop all registered plugins
* @private
*/
Core.prototype.stopPlugins = function() {
for (var i = 0, l = this.aPlugins.length; i < l; i++) {
var oPlugin = this.aPlugins[i];
if (oPlugin && oPlugin.stopPlugin) {
oPlugin.stopPlugin(this);
}
}
};
/**
* Sets or unsets a model for the given model name.
*
* The <code>sName</code> must either be <code>undefined</code> (or omitted) or a non-empty string.
* When the name is omitted, the default model is set/unset.
*
* When <code>oModel</code> is <code>null</code> or <code>undefined</code>, a previously set model
* with that name is removed from the Core.
*
* Any change (new model, removed model) is propagated to all existing UIAreas and their descendants
* as long as a descendant doesn't have its own model set for the given name.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* @param {sap.ui.model.Model} oModel the model to be set or <code>null</code> or <code>undefined</code>
* @param {string} [sName] the name of the model or <code>undefined</code>
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.setModel = function(oModel, sName) {
jQuery.sap.assert(oModel == null || oModel instanceof sap.ui.model.Model, "oModel must be an instance of sap.ui.model.Model, null or undefined");
jQuery.sap.assert(sName === undefined || (typeof sName === "string" && !/^(undefined|null)?$/.test(sName)), "sName must be a string or omitted");
if (!oModel && this.oModels[sName]) {
delete this.oModels[sName];
// propagate Models to all UI areas
jQuery.each(this.mUIAreas, function (i, oUIArea){
delete oUIArea.oPropagatedProperties.oModels[sName];
oUIArea.propagateProperties(sName);
});
} else if (oModel && oModel !== this.oModels[sName] ) {
this.oModels[sName] = oModel;
// propagate Models to all UI areas
jQuery.each(this.mUIAreas, function (i, oUIArea){
oUIArea.oPropagatedProperties.oModels[sName] = oModel;
oUIArea.propagateProperties(sName);
});
} //else nothing to do
return this;
};
Core.prototype.setMessageManager = function(oMessageManager) {
this.oMessageManager = oMessageManager;
};
Core.prototype.getMessageManager = function() {
if (!this.oMessageManager) {
this.oMessageManager = new MessageManager();
}
return this.oMessageManager;
};
/**
* Get the model with the given model name.
*
* The name can be omitted to reference the default model or it must be a non-empty string.
*
* Note: to be compatible with future versions of this API, applications must not use the value <code>null</code>,
* the empty string <code>""</code> or the string literals <code>"null"</code> or <code>"undefined"</code> as model name.
*
* @param {string|undefined} [sName] name of the model to be retrieved
* @return {sap.ui.model.Model} oModel
* @public
*/
Core.prototype.getModel = function(sName) {
jQuery.sap.assert(sName === undefined || (typeof sName === "string" && !/^(undefined|null)?$/.test(sName)), "sName must be a string or omitted");
return this.oModels[sName];
};
/**
* Check if a Model is set to the core
* @return {boolean} true or false
* @public
*/
Core.prototype.hasModel = function() {
return !jQuery.isEmptyObject(this.oModels);
};
/**
* Returns the event bus.
* @return {sap.ui.core.EventBus} the event bus
* @since 1.8.0
* @public
*/
Core.prototype.getEventBus = function() {
if (!this.oEventBus) {
jQuery.sap.require("sap.ui.core.EventBus");
this.oEventBus = new sap.ui.core.EventBus();
}
return this.oEventBus;
};
/**
* Attach event-handler <code>fnFunction</code> to the 'validationError' event of <code>sap.ui.core.Core</code>.<br/>
* Please note that this event is a bubbling event and may already be canceled before reaching the core.<br/>
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this Model is used.
*
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.attachValidationError = function(oData, fnFunction, oListener) {
if (typeof (oData) === "function") {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
this.attachEvent(Core.M_EVENTS.ValidationError, oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'validationError' event of <code>sap.ui.core.Core</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The callback function to unregister
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.detachValidationError = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.ValidationError, fnFunction, oListener);
return this;
};
/**
* Attach event-handler <code>fnFunction</code> to the 'parseError' event of <code>sap.ui.core.Core</code>.<br/>
* Please note that this event is a bubbling event and may already be canceled before reaching the core.<br/>
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this Model is used.
*
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.attachParseError = function(oData, fnFunction, oListener) {
if (typeof (oData) === "function") {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
this.attachEvent(Core.M_EVENTS.ParseError, oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'parseError' event of <code>sap.ui.core.Core</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The callback function to unregister.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.detachParseError = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.ParseError, fnFunction, oListener);
return this;
};
/**
* Attach event-handler <code>fnFunction</code> to the 'formatError' event of <code>sap.ui.core.Core</code>.<br/>
* Please note that this event is a bubbling event and may already be canceled before reaching the core.<br/>
*
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this Model is used.
*
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.attachFormatError = function(oData, fnFunction, oListener) {
if (typeof (oData) === "function") {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
this.attachEvent(Core.M_EVENTS.FormatError, oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'formatError' event of <code>sap.ui.core.Core</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The callback function to unregister
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.detachFormatError = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.FormatError, fnFunction, oListener);
return this;
};
/**
* Attach event-handler <code>fnFunction</code> to the 'validationSuccess' event of <code>sap.ui.core.Core</code>.<br/>
* Please note that this event is a bubbling event and may already be canceled before reaching the core.<br/>
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this Model is used.
*
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.attachValidationSuccess = function(oData, fnFunction, oListener) {
if (typeof (oData) === "function") {
oListener = fnFunction;
fnFunction = oData;
oData = undefined;
}
this.attachEvent(Core.M_EVENTS.ValidationSuccess, oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'validationSuccess' event of <code>sap.ui.core.Core</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @public
*/
Core.prototype.detachValidationSuccess = function(fnFunction, oListener) {
this.detachEvent(Core.M_EVENTS.ValidationSuccess, fnFunction, oListener);
return this;
};
/**
* Fire event parseError to attached listeners.
*
* Expects following event parameters:
* <ul>
* <li>'element' of type <code>sap.ui.core.Element</code> </li>
* <li>'property' of type <code>string</code> </li>
* <li>'type' of type <code>string</code> </li>
* <li>'newValue' of type <code>object</code> </li>
* <li>'oldValue' of type <code>object</code> </li>
* <li>'exception' of type <code>object</code> </li>
* </ul>
*
* @param {Map} [mArguments] the arguments to pass along with the event.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @protected
*/
Core.prototype.fireParseError = function(mArguments) {
this.fireEvent(Core.M_EVENTS.ParseError, mArguments);
return this;
};
/**
* The 'parseError' event is fired when input parsing fails.
*
* @name sap.ui.core.Core#parseError
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {sap.ui.core.Element} oControlEvent.getParameters.element The Element where the parse error occurred
* @param {string} oControlEvent.getParameters.property The property name of the element where the parse error occurred
* @param {type} oControlEvent.getParameters.type The type of the property
* @param {object} oControlEvent.getParameters.newValue The value of the property which was entered when the parse error occurred
* @param {object} oControlEvent.getParameters.oldValue The value of the property which was present before a new value was entered (before the parse error)
* @param {object} oControlEvent.getParameters.exception The exception object which occurred and has more information about the parse error
* @public
*/
/**
* Fire event validationError to attached listeners.
*
* Expects following event parameters:
* <ul>
* <li>'element' of type <code>sap.ui.core.Element</code> </li>
* <li>'property' of type <code>string</code> </li>
* <li>'type' of type <code>string</code> </li>
* <li>'newValue' of type <code>object</code> </li>
* <li>'oldValue' of type <code>object</code> </li>
* <li>'exception' of type <code>object</code> </li>
* </ul>
*
* @param {Map} [mArguments] the arguments to pass along with the event.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @protected
*/
Core.prototype.fireValidationError = function(mArguments) {
this.fireEvent(Core.M_EVENTS.ValidationError, mArguments);
return this;
};
/**
* The 'validationError' event is fired when validation of the input fails.
*
* @name sap.ui.core.Core#validationError
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {sap.ui.core.Element} oControlEvent.getParameters.element The Element where the validation error occurred
* @param {string} oControlEvent.getParameters.property The property name of the element where the validation error occurred
* @param {type} oControlEvent.getParameters.type The type of the property
* @param {object} oControlEvent.getParameters.newValue The value of the property which was entered when the validation error occurred
* @param {object} oControlEvent.getParameters.oldValue The value of the property which was present before a new value was entered (before the validation error)
* @param {object} oControlEvent.getParameters.exception The exception object which occurred and has more information about the validation error
* @public
*/
/**
* Fire event formatError to attached listeners.
*
* Expects following event parameters:
* <ul>
* <li>'element' of type <code>sap.ui.core.Element</code> </li>
* <li>'property' of type <code>string</code> </li>
* <li>'type' of type <code>string</code> </li>
* <li>'newValue' of type <code>object</code> </li>
* <li>'oldValue' of type <code>object</code> </li>
* <li>'exception' of type <code>object</code> </li>
* </ul>
*
* @param {Map} [mArguments] the arguments to pass along with the event.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @protected
*/
Core.prototype.fireFormatError = function(mArguments) {
this.fireEvent(Core.M_EVENTS.FormatError, mArguments);
return this;
};
/**
* The 'formatError' event is fired when a value formatting fails. This can happen when a value stored in the model cannot be formatted to be displayed in an element property.
*
* @name sap.ui.core.Core#formatError
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {sap.ui.core.Element} oControlEvent.getParameters.element The Element where the format error occurred
* @param {string} oControlEvent.getParameters.property The property name of the element where the format error occurred
* @param {type} oControlEvent.getParameters.type The type of the property
* @param {object} oControlEvent.getParameters.newValue The value of the property which was entered when the format error occurred
* @param {object} oControlEvent.getParameters.oldValue The value of the property which was present before a new value was entered (before the format error)
* @param {object} oControlEvent.getParameters.exception The exception object which occurred and has more information about the format error
* @public
*/
/**
* Fire event validationSuccess to attached listeners.
*
* Expects following event parameters:
* <ul>
* <li>'element' of type <code>sap.ui.core.Element</code> </li>
* <li>'property' of type <code>string</code> </li>
* <li>'type' of type <code>string</code> </li>
* <li>'newValue' of type <code>object</code> </li>
* <li>'oldValue' of type <code>object</code> </li>
* </ul>
*
* @param {Map} [mArguments] the arguments to pass along with the event.
* @return {sap.ui.core.Core} <code>this</code> to allow method chaining
* @protected
*/
Core.prototype.fireValidationSuccess = function(mArguments) {
this.fireEvent(Core.M_EVENTS.ValidationSuccess, mArguments);
return this;
};
/**
* The 'validationSuccess' event is fired when a value validation was successfully completed.
*
* @name sap.ui.core.Core#validationSuccess
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {sap.ui.core.Element} oControlEvent.getParameters.element The Element where the successful validation occurred
* @param {string} oControlEvent.getParameters.property The property name of the element where the successfull validation occurred
* @param {type} oControlEvent.getParameters.type The type of the property
* @param {object} oControlEvent.getParameters.newValue The value of the property which was entered when the validation occurred
* @param {object} oControlEvent.getParameters.oldValue The value of the property which was present before a new value was entered (before the validation)
* @public
*/
/**
* Check if the script is running on mobile
* @return {boolean} true or false
* @public
*/
Core.prototype.isMobile = function() {
return Device.browser.mobile;
};
/**
* @name sap.ui.core.CorePlugin
* @interface Contract for plugins that want to extend the core runtime
*/
/**
* Called by the Core after it has been initialized.
* If a plugin is added to the core after its initialization, then
* this method is called during registration of the plugin.
*
* Implementing this method is optional for a plugin.
*
* @name sap.ui.core.CorePlugin.prototype.startPlugin
* @param {sap.ui.core.Core} oCore reference to the core
* @param {boolean} bOnInit whether the hook is called during Core.init() or later
* @function
*/
/**
* Called by the Core when it is shutdown or when a plugin is
* deregistered from the core.
*
* Implementing this method is optional for a plugin.
*
* @name sap.ui.core.CorePlugin.prototype.stopPlugin
* @param {sap.ui.core.Core} oCore reference to the core
* @function
*/
/**
* Displays the control tree with the given root inside the area of the given
* DOM reference (or inside the DOM node with the given ID) or in the given Control.
*
* Example:
* <pre>
* &lt;div id="SAPUI5UiArea">&lt;/div>
* &lt;script type="text/javascript">
* var oRoot = new sap.ui.commons.Label();
* oRoot.setText("Hello world!");
* sap.ui.setRoot("SAPUI5UiArea", oRoot);
* &lt;/script>
* </pre>
* <p>
*
* This is a shortcut for <code>sap.ui.getCore().setRoot()</code>.
*
* Internally, if a string is given that does not identify an UIArea or a control
* then implicitly a new <code>UIArea</code> is created for the given DOM reference
* and the given control is added.
*
* @param {string|Element|sap.ui.core.Control} oDomRef a DOM Element or Id String of the UIArea
* @param {sap.ui.base.Interface | sap.ui.core.Control}
* oControl the Control that should be added to the <code>UIArea</code>.
* @public
* @deprecated Use function <code>placeAt</code> of <code>sap.ui.core.Control</code> instead.
*/
sap.ui.setRoot = function(oDomRef, oControl) {
jQuery.sap.assert(typeof oDomRef === "string" || typeof oDomRef === "object", "oDomRef must be a string or object");
jQuery.sap.assert(oControl instanceof sap.ui.base.Interface || oControl instanceof Control, "oControl must be a Control or Interface");
sap.ui.getCore().setRoot(oDomRef, oControl);
};
/*
* Create a new (the only) instance of the Core and return it's interface as module value.
*
* Do not export the module value under the global name!
*
* Note that the Core = EventProvider.extend() call above already exposes sap.ui.core.Core.
* This is needed for backward compatibility reason, in case some other code tries to enhance
* the core prototype. Once global names are switched off, such extension scnearios are
* no longer supported.
*/
return new Core().getInterface();
}, /* bExport= */ false);
},
"sap/ui/core/CustomStyleClassSupport.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides helper sap.ui.core.CustomStyleClassSupport
sap.ui.define(['jquery.sap.global', './Element'],
function(jQuery, Element) {
"use strict";
/**
* If called on the prototype of a sap.ui.core.Element, the Element (and its prototype) is extended
* to support custom style classes.
*
* WARNING: only to be used WITHIN a control implementation. An application cannot add style class support by calling this method!
*
* The methods "addStyleClass", "removeStyleClass", toggleStyleClass and "hasStyleClass" are added to the Element and the clone() method
* is extended to also clone the custom classes.
*
* Usage example:
* jQuery.sap.require("sap.ui.core.CustomStyleClassSupport");
* sap.ui.core.CustomStyleClassSupport.apply(sap.ui.commons.layout.MatrixLayoutCell.prototype);
*
* Each renderer handling the given Element MUST then call
* renderManager.writeClasses(oElement);
* when writing the root tag of the Element. This makes sure the classes are written to the HTML.
*
* @public
* @alias sap.ui.core.CustomStyleClassSupport
* @function
*/
var CustomStyleClassSupport = function () {
// "this" is the prototype now when called with apply()
// Ensure only Elements are enhanced
if (!(this instanceof Element)) {
return;
}
// enrich original clone function
var fOriginalClone = this.clone;
this.clone = function() {
// call original clone function
var oClone = fOriginalClone.apply(this, arguments);
// add the style classes of "this" to the clone
if (this.aCustomStyleClasses) {
oClone.aCustomStyleClasses = this.aCustomStyleClasses.slice();
}
return oClone;
};
this.addStyleClass = function(sStyleClass, bSuppressRerendering) { // bSuppressRerendering is experimental and hence undocumented
jQuery.sap.assert(sStyleClass && typeof sStyleClass === "string", "sStyleClass must be a string");
if (!this.aCustomStyleClasses) {
this.aCustomStyleClasses = [];
}
if (sStyleClass) {
// ensure the "class" attribute is not closed
if (sStyleClass.indexOf("\"") > -1) {
return this;
}
if (sStyleClass.indexOf("'") > -1) {
return this;
} // TODO: maybe check for quotes in different charsets or encodings
// multiple calls should not add the class multiple times
for (var i = this.aCustomStyleClasses.length - 1; i >= 0; i--) {
if (this.aCustomStyleClasses[i] == sStyleClass) {
return this;
}
}
this.aCustomStyleClasses.push(sStyleClass);
var oRoot = this.getDomRef();
if (oRoot) { // non-rerendering shortcut
jQuery(oRoot).addClass(sStyleClass);
} else if (bSuppressRerendering === false) {
this.invalidate();
}
}
return this;
};
this.removeStyleClass = function(sStyleClass, bSuppressRerendering) { // bSuppressRerendering is experimental and hence undocumented
jQuery.sap.assert(sStyleClass && typeof sStyleClass === "string", "sStyleClass must be a string");
if (sStyleClass && this.aCustomStyleClasses) {
for (var i = this.aCustomStyleClasses.length - 1; i >= 0; i--) {
if (this.aCustomStyleClasses[i] == sStyleClass) {
this.aCustomStyleClasses.splice(i, 1);
var oRoot = this.getDomRef();
if (oRoot) { // non-rerendering shortcut
jQuery(oRoot).removeClass(sStyleClass);
} else if (bSuppressRerendering === false) {
this.invalidate();
}
}
}
}
return this;
};
this.toggleStyleClass = function(sStyleClass, bAdd) {
jQuery.sap.assert(sStyleClass && typeof sStyleClass === "string", "sStyleClass must be a string");
if (sStyleClass && typeof sStyleClass === "string") {
if (bAdd === true) {
this.addStyleClass(sStyleClass);
} else if (bAdd === false) {
this.removeStyleClass(sStyleClass);
} else if (bAdd === undefined) {
this.hasStyleClass(sStyleClass) ? this.removeStyleClass(sStyleClass) : this.addStyleClass(sStyleClass);
} else {
jQuery.sap.log.warning(this.toString() + "- toggleStyleClass(): bAdd should be a boolean or undefined, but is '" + bAdd + "'");
}
}
return this; // we could (depending on bAdd) return either this or the boolean result of removeStyleClass, but at least in the bAdd===undefined case the caller wouldn't even know which return type to expect...
};
this.hasStyleClass = function(sStyleClass) {
jQuery.sap.assert(sStyleClass && typeof sStyleClass === "string", "sStyleClass must be a string");
if (sStyleClass && this.aCustomStyleClasses) {
for (var i = this.aCustomStyleClasses.length - 1; i >= 0; i--) {
if (this.aCustomStyleClasses[i] == sStyleClass) {
return true;
}
}
}
return false;
};
this.getMetadata().addPublicMethods(["addStyleClass", "removeStyleClass", "toggleStyleClass", "hasStyleClass"]);
};
// moved here to fix the cyclic dependency CustomStyleClassSupport, Element, Core, Control
return CustomStyleClassSupport;
}, /* bExport= */ true);
},
"sap/ui/core/Element.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base class for all controls and UI elements.
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObject', './Core', './ElementMetadata', 'sap/ui/model/SimpleType', 'jquery.sap.strings'],
function(jQuery, ManagedObject, Core, ElementMetadata, SimpleType/* , jQuerySap */) {
"use strict";
/**
* Constructs and initializes an UI Element with the given <code>sId</code> and settings.
*
* If the optional <code>mSettings</code> are given, they must be a JSON-like object (object literal)
* that defines values for properties, aggregations, associations or events keyed by their name.
*
* <b>Valid Names:</b>
*
* The property (key) names supported in the object literal are exactly the (case sensitive)
* names documented in the JSDoc for the properties, aggregations, associations and events
* of the control and its base classes. Note that for 0..n aggregations and associations this
* usually is the plural name, whereas it is the singular name in case of 0..1 relations.
*
* If a key name is ambiguous for a specific control class (e.g. a property has the same
* name as an event), then this method prefers property, aggregation, association and
* event in that order. To resolve such ambiguities, the keys can be prefixed with
* <code>aggregation:</code>, <code>association:</code> or <code>event:</code>.
* In that case the keys must be quoted due to the ':'.
*
* Each subclass should document the set of supported names in its constructor documentation.
*
* <b>Valid Values:</b>
*
* <ul>
* <li>for normal properties, the value has to be of the correct simple type (no type conversion occurs)
* <li>for 0..1 aggregations, the value has to be an instance of the aggregated control or element type
* <li>for 0..n aggregations, the value has to be an array of instances of the aggregated type
* <li>for 0..1 associations, an instance of the associated type or an id (string) is accepted
* <li>0..n associations are not supported yet
* <li>for events either a function (event handler) is accepted or an array of length 2
* where the first element is a function and the 2nd element is an object to invoke the method on.
* </ul>
*
* Special aggregation "dependents" is connected to the lifecycle management and databinding,
* but not rendered automatically and can be used for popups or other dependent controls. This allows
* definition of popup controls in declarative views and enables propagation of model and context
* information to them.
*
* @param {string} [sId] id for the new control; generated automatically if no non-empty id is given
* Note: this can be omitted, no matter whether <code>mSettings</code> will be given or not!
* @param {object} [mSettings] optional map/JSON-object with initial property values, aggregated objects etc. for the new element
*
* @class Base Class for Elements.
* @extends sap.ui.base.ManagedObject
* @author SAP SE
* @version 1.28.5
* @public
* @alias sap.ui.core.Element
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var Element = ManagedObject.extend("sap.ui.core.Element", {
metadata : {
stereotype : "element",
"abstract" : true,
publicMethods : [ "getId", "getMetadata", "getTooltip_AsString", "getTooltip_Text", "getModel", "setModel", "hasModel", "bindElement", "unbindElement", "getElementBinding", "prop", "getLayoutData", "setLayoutData" ],
library : "sap.ui.core",
aggregations : {
/**
* The tooltip that should be shown for this Element.
*
* Can either be an instance of a TooltipBase subclass or a simple string.
*/
tooltip : {name : "tooltip", type : "sap.ui.core.TooltipBase", altTypes : ["string"], multiple : false},
/**
* Custom Data, a data structure like a map containing arbitrary key value pairs.
*/
customData : {name : "customData", type : "sap.ui.core.CustomData", multiple : true, singularName : "customData"},
/**
* Defines the layout constraints for this control when it is used inside a Layout.
* LayoutData classes are typed classes and must match the embedding Layout.
* See VariantLayoutData for aggregating multiple alternative LayoutData instances to a single Element.
*/
layoutData : {name : "layoutData", type : "sap.ui.core.LayoutData", multiple : false, singularName : "layoutData"},
/**
* Dependents are not rendered, but their databinding context and lifecycle are bound to the aggregating Element.
* @since 1.19
*/
dependents : {name : "dependents", type : "sap.ui.core.Control", multiple : true}
}
},
constructor : function(sId, mSettings) {
ManagedObject.apply(this, arguments);
},
renderer : null // Element has no renderer
}, /* Metadata constructor */ ElementMetadata);
/**
* Creates a new subclass of class sap.ui.core.Element with name <code>sClassName</code>
* and enriches it with the information contained in <code>oClassInfo</code>.
*
* <code>oClassInfo</code> might contain the following:
* <ul>
* <li><code>metadata:</code> an (optional) object literal containing meta information about the class.
* The information in the object literal will be wrapped by an instance of ElementMetadata
* and might contain the following information:
* <ul>
* <li>all values accepted for metadata as documented for the {@link sap.ui.base.Object.extend Object.extend} method</li>
* <li>library: {string} (optional) name of the library that contains the element/control
* <li>properties: a map of property info objects, mapped by the property name
* Each info object should be a simple object literal and may contain the following information
* <ul>
* <li>type {string} optional type of the property, defaults to type "string"
* <li>[defaultValue] {any} default value of the property. When omitted, defaults to the default value for the type
* <li>group {string} optional semantic group of the property. Defaults to "Misc"
* </ul>
* If the property info object is not an object literal, it is assumed to be a string and
* interpreted as the type of the property. All other informations use their default values.
* <li>aggregations: a map of aggregation info objects, mapped by the aggregation name
* By convention, the name should be a singular name for aggregations of cardinality 0..1 and
* should be a plural name for aggregations of cardinality 0..n.
*
* The info object should contain the following information
* <ul>
* <li>type {string} (optional) type of the aggregated controls/elements, defaults to sap.ui.core.Control
* <li>altTypes {string[]} (optional) alternative primitive types that the aggregation can have (like string etc.). Defaults to no alternative types.
* <li>multiple {boolean} (optional) information about the cardinality, defaults to true (multiple aggregation)
* <li>singularName {string} (optional) singular name for 0..n aggregations. If not specified, a singular name is guessed from the plural name.
* </ul>
* If the aggregation info object is not an object literal, it is assumed to be a string and
* interpreted as the type of the aggregation. All other informations use their default values.
* <li>associations: a map of association info objects, mapped by the association name
* By convention, the name should be a singular name for aggregations of cardinality 0..1 and
* should be a plural name for aggregations of cardinality 0..n.
*
* The info object should contain the following information
* <ul>
* <li>type {string} type of the associated controls/elements, defaults to sap.ui.core.Control
* <li>multiple {boolean} (optional) information about the cardinality, defaults to false (single aggregation)
* <li>singularName {string} (optional) singular name for 0..n aggregations. If not specified, a singular name is guessed from the plural name.
* </ul>
* If the association info object is not an object literal, it is assumed to be a string and
* interpreted as the type of the aggregation. All other informations then use their default values.
* <li>events: a map of event info objects, mapped by the event name
* The info object can contain the following information
* <ul>
* <li><code>allowPreventDefault:</code> {boolean} whether the control allows to prevent its default behavior for this event (defaults to false) </li>
* </ul>
* </ul>
* For all properties, aggregations, associations and events, the usual access methods are created and added to the public facade.
* They don't need to be listed in the publicMethods array.
* </li>
*
* <li><code>constructor:</code> a function that serves as a constructor function for the new element class.
* If no constructor function is given, the framework creates a default implementation that delegates all
* its arguments to the constructor function of the base class.
* <b>Note:</b> most of the time, subclasses of Element don't need to specify their own constructor function.
* They should use the symmetric {@link #init} and {@link #exit} hooks instead.
* </li>
*
* <li><code>renderer:</code> definition of a renderer. This can be any of the following
* <ul>
* <li>the class name of a renderer class (a string)</li>
* <li>a render function with signature <code>function(oRenderManager, oControl)</code> that implements the rendering for the new class</li>
* <li>an object literal that contains functions/properties that should be mixed into a new render class which is
* created as a subclass of the renderer of the current class.</li>
* </ul>
* <b>Note:</b> usually only controls have a renderer. But to keep the control creation APIs simple and to honor future
* extensions of the current policy, the definition of a renderer is supported for direct subclasses of Element as well.
* </li>
*
* <li><i>any-other-name:</i> any other property in the <code>oClassInfo</code> is copied into the prototype
* object of the newly created class. Callers can thereby add methods or properties to all instances of the
* class. But be aware that the given values are shared between all instances of the class. Usually, it doesn't
* make sense to use primitive values here other than to declare public constants.
*
* All methods added this way and whose name is not 'init' nor 'exit' nor does it start with an underscore ('_')
* nor with the prefix 'on' are assumed to be public methods and are automatically added to the list of public facade methods.
* </ul>
*
* The prototype object of the newly created class uses the same prototype as instances of the base class
* (prototype chaining).
*
* A metadata object is always created, even if there is no <code>metadata</code> entry in the <code>oClassInfo</code>
* object. A getter for the metadata is always attached to the prototype and to the class (constructor function)
* itself.
*
* Last but not least, with the third argument <code>FNMetaImpl</code> the constructor of a metadata class
* can be specified. Instances of that class will be used to represent metadata for the newly created class
* and for any subclass created from it. Typically, only frameworks will use this parameter to enrich the
* metadata for a new class hierarchy they introduce (as done by {@link sap.ui.core.Element Element}).
*
* @param {string} sClassName name of the class to be created
* @param {object} [oClassInfo] structured object with informations about the class
* @param {function} [FNMetaImpl] constructor function for the metadata object. If not given, it defaults to sap.ui.core.ElementMetadata.
* @return {function} the created class / constructor function
* @public
* @static
* @name sap.ui.core.Element.extend
* @function
* @since 1.3.1
*/
/**
* Creates metadata for an UI Element by extending the Object Metadata.
*
* In addition to the entries defined by {@link sap.ui.base.Object.defineClass}, the following
* entries can be specified in the static info object:
*
* <ul>
* <li>library: {string} name of the library that contains the element/control
* <li>properties: a map of property info objects, mapped by the property name
* Info object should contain the following information
* <ul>
* <li>name {string} name of the property (redundant to map key)
* <li>type {string} type of the property
* <li>[defaultValue] {any} default value of the property. Can be omitted
* </ul>
* <li>aggregations: a map of aggregation info objects, mapped by the aggregation name
* Info object should contain the following information
* <ul>
* <li>name {string} name of the aggregation, singular for 0..1, plural for 0..n
* <li>type {string} type of the aggregated controls/elements
* <li>multiple {boolean}
* <li>singularName {string} singular name for 0..n aggregations
* </ul>
* <li>associations: a map of association info objects, mapped by the association name
* Info object should contain the following information
* <ul>
* <li>name {string} name of the association, singular for 0..1, plural for 0..n
* <li>type {string} type of the associated controls/elements
* <li>multiple {boolean}
* <li>singularName {string} singular name for 0..n associations
* </ul>
* <li>events: map from event names to event names
* </ul>
*
* @see sap.ui.core.Object.defineClass
*
* @param {string} sClassName name of the class to build the metadata for
* @param {object} oStaticInfo static information used to build the metadata
* @param {function} [fnMetaImpl] constructor to be used for the metadata
* @return {object} the created metadata
* @static
* @public
* @deprecated Since 1.3.1. Use the static <code>extend</code> method of the desired base class (e.g. {@link sap.ui.core.Element.extend})
*/
Element.defineClass = function(sClassName, oStaticInfo, fnMetaImpl) {
// create and attach metadata but with an Element specific implementation
return sap.ui.base.Object.defineClass(sClassName, oStaticInfo, fnMetaImpl || ElementMetadata);
};
/**
* @see sap.ui.base.Object#getInterface
* @public
*/
Element.prototype.getInterface = function() {
return this;
};
/**
* Handles the given browser event.
* @private
*/
Element.prototype._handleEvent = function (oEvent) {
var sHandlerName = "on" + oEvent.type;
this._callEventHandles(this.aBeforeDelegates.slice(0), sHandlerName, oEvent, true);
this._callEventHandles([this], sHandlerName, oEvent);
this._callEventHandles(this.aDelegates.slice(0), sHandlerName, oEvent, true);
};
/**
* Calls event handler of the given event handles with the given browser event.
* @private
*/
Element.prototype._callEventHandles = function (aHandles, sHandlerName, oEvent, bDelegateHolder) {
if (aHandles.length > 0) {
for (var i = 0; i < aHandles.length; i++) {
if (oEvent.isImmediateHandlerPropagationStopped()) {
break;
}
var oHandle = bDelegateHolder ? aHandles[i].oDelegate : aHandles[i];
var oThis = (bDelegateHolder && aHandles[i].vThis) ? aHandles[i].vThis : oHandle;
if (oThis === true) { // special case, means the control should be the context
oThis = this;
}
if (oHandle[sHandlerName]) {
oHandle[sHandlerName].call(oThis, oEvent);
}
}
}
};
// Element is granted "friend" access by Core for (de-)registration
/**
* Registers this instance of sap.ui.core.Element with the Core.
*
* The implementation of this method is provided with "friend" access by Core.
* @see sap.ui.core.Core.constructor
*
* @function
* @name sap.ui.core.Element.prototype.register
* @private
*/
//sap.ui.core.Element.prototype.register = function() {...}
/**
* Deregisters this instance of sap.ui.core.Element from the Core.
*
* The implementation of this method is provided with "friend" access by Core.
* @see sap.ui.core.Core.constructor
*
* @function
* @name sap.ui.core.Element.prototype.deregister
* @private
*/
//sap.ui.core.Element.prototype.deregister = function() {...}
/**
* Initializes the element instance after creation.
*
* Applications must not call this hook method directly, it is called by the framework
* while the constructor of an element is executed.
*
* Subclasses of Element should override this hook to implement any necessary initialization.
*
* @function
* @name sap.ui.core.Element.prototype.init
* @protected
*/
//sap.ui.core.Element.prototype.init = function() {};
/**
* Cleans up the element instance before destruction.
*
* Applications must not call this hook method directly, it is called by the framework
* when the element is {@link #destroy destroyed}.
*
* Subclasses of Element should override this hook to implement any necessary cleanup.
*
* @function
* @name sap.ui.core.Element.prototype.exit
* @protected
*/
//sap.ui.core.Element.prototype.exit = function() {};
/**
* Creates a new Element from the given data.
*
* If vData is an Element already, that element is returned.
* If vData is an object (literal), then a new element is created with vData as settings.
* The type of the element is either determined by a "Type" entry in the vData or
* by a type information in the oKeyInfo object
* @param {sap.ui.core.Element|object} vData the data to create the element from
* @param {object} [oKeyInfo] an entity information (e.g. aggregation info)
* @param {string} [oKeyInfo.type] type info for the entity
* @public
* @static
*/
Element.create = function(vData, oKeyInfo) {
if ( !vData || vData instanceof Element || typeof vData !== "object" || vData instanceof String) {
return vData;
}
function getClass(vType) {
if ( typeof vType === "function" ) {
return vType;
}
if (typeof vType === "string" ) {
return jQuery.sap.getObject(vType);
}
}
var fnClass = getClass(vData.Type) || getClass(oKeyInfo && oKeyInfo.type);
if ( typeof fnClass === "function" ) {
return new fnClass(vData);
}
// we don't know how to create the Element from vData, so fail
// extension points could be integrated here
var message = "Don't know how to create an Element from " + vData + " (" + (typeof vData) + ")";
jQuery.sap.log.fatal(message);
throw new Error(message);
};
/**
* Returns a simple string representation of this element.
*
* Mainly useful for tracing purposes.
* @public
* @return {string} a string descripition of this element
*/
Element.prototype.toString = function() {
if ( this.getMetadata ) {
return "Element " + this.getMetadata().getName() + "#" + this.sId;
} else {
return "Element {unknown class}#" + this.sId;
}
};
/**
* Returns the best suitable DOM Element that represents this UI5 Element.
* By default the DOM Element with the same ID as this Element is returned.
* Subclasses should override this method if the lookup via id is not sufficient.
*
* Note that such a DOM Element does not necessarily exist in all cases.
* Some elements or controls might not have a DOM representation at all (e.g.
* a naive FlowLayout) while others might not have one due to their current
* state (e.g. an initial, not yet rendered control).
*
* If an ID suffix is given, the ID of this Element is concatenated with the suffix
* (separated by a single dash) and the DOM node with that compound ID will be returned.
* This matches the UI5 naming convention for named inner DOM nodes of a control.
*
* @param {string} [sSuffix] ID suffix to get the DOMRef for
* @return {Element} The Element's DOM Element sub DOM Element or null
* @protected
*/
Element.prototype.getDomRef = function(sSuffix) {
return jQuery.sap.domById(sSuffix ? this.getId() + "-" + sSuffix : this.getId());
};
/**
* Returns the best suitable DOM node that represents this Element wrapped as jQuery object.
* I.e. the element returned by {@link sap.ui.core.Element#getDomRef} is wrapped and returned.
*
* If an ID suffix is given, the ID of this Element is concatenated with the suffix
* (separated by a single dash) and the DOM node with that compound ID will be wrapped by jQuery.
* This matches the UI5 naming convention for named inner DOM nodes of a control.
*
* @param {string} [sSuffix] ID suffix to get a jQuery object for
* @return {jQuery} The jQuery wrapped element's DOM reference
* @protected
*/
Element.prototype.$ = function(sSuffix) {
return jQuery(this.getDomRef(sSuffix));
};
/**
* Checks whether this element has an active parent.
*
* @type boolean
* @return true if this element has an active parent
* @private
*/
Element.prototype.isActive = function() {
return this.oParent && this.oParent.isActive();
};
/**
* This function either calls set[sPropertyName] or get[sPropertyName] with the specified property name
* depending if an <code>oValue</code> is provided or not.
*
* @param {string} sPropertyName name of the property to set
* @param {any} [oValue] value to set the property to
* @return {any|sap.ui.core.Element} Returns <code>this</code> to allow method chaining in case of setter and the property value in case of getter
* @public
* @deprecated Since 1.28.0 The contract of this method is not fully defined and its write capabilities overlap with applySettings
*/
Element.prototype.prop = function(sPropertyName, oValue) {
var oPropertyInfo = this.getMetadata().getAllSettings()[sPropertyName];
if (oPropertyInfo) {
if (arguments.length == 1) {
// getter
return this[oPropertyInfo._sGetter]();
} else {
// setter
this[oPropertyInfo._sMutator](oValue);
return this;
}
}
};
Element.prototype.insertDependent = function(oControl, iIndex) {
return this.insertAggregation("dependents", oControl, iIndex, true);
};
Element.prototype.addDependent = function(oControl) {
return this.addAggregation("dependents", oControl, true);
};
Element.prototype.removeDependent = function(vControl) {
return this.removeAggregation("dependents", vControl, true);
};
Element.prototype.removeAllDependents = function() {
return this.removeAllAggregation("dependents", true);
};
Element.prototype.destroyDependents = function() {
return this.destroyAggregation("dependents", true);
};
/// cyclic dependency
//jQuery.sap.require("sap.ui.core.TooltipBase"); /// cyclic dependency
/**
* This triggers immediate rerendering of its parent and thus of itself and its children.<br/> As <code>sap.ui.core.Element</code> "bubbles up" the
* rerender, changes to child-<code>Elements</code> will also result in immediate rerendering of the whole sub tree.
* @protected
*/
Element.prototype.rerender = function() {
if (this.oParent) {
this.oParent.rerender();
}
};
/**
* Returns the UI area of this element, if any.
*
* @return {sap.ui.core.UIArea} The UI area of this element or null
* @private
*/
Element.prototype.getUIArea = function() {
return this.oParent ? this.oParent.getUIArea() : null;
};
/**
* Cleans up the resources associated with this element and all its children.
*
* After an element has been destroyed, it can no longer be used in the UI!
*
* Applications should call this method if they don't need the element any longer.
*
* @param {boolean}
* [bSuppressInvalidate] if true, the UI element is not marked for redraw
* @public
*/
Element.prototype.destroy = function(bSuppressInvalidate) {
ManagedObject.prototype.destroy.call(this, bSuppressInvalidate);
// remove this control from DOM, e.g. if there is no parent (e.g. Dialog or already removed control) or this.sParentAggregationName is not properly set
this.$().remove();
};
/**
* Fires the given event and notifies all listeners. Listeners must not change
* the content of the event.
*
* @param {string} sEventId the event id
* @param {object} mParameters the parameter map
* @return {sap.ui.core.Element} Returns <code>this</code> to allow method chaining
* @protected
*/
Element.prototype.fireEvent = function(sEventId, mParameters) {
// clone 'arguments' and modify clone to be strict mode compatible
var aArgs = Array.prototype.slice.apply(arguments);
// TODO 'id' is somewhat redundant to getSource(), but it is commonly used - fade out with next major release?
aArgs[1] = mParameters = mParameters || {};
mParameters.id = mParameters.id || this.getId();
// 'aArgs' is necessary, as the EventProvider.fireEvent signature has more parameters
return sap.ui.base.EventProvider.prototype.fireEvent.apply(this, aArgs);
};
/**
* Adds a delegate that listens to the events of this element.
*
* Note that the default behavior (delegate attachments are not cloned when a control is cloned) is usually the desired behavior in control development
* where each control instance typically creates a delegate and adds it to itself. (As opposed to application development where the application may add
* one delegate to a template and then expects aggregation binding to add the same delegate to all cloned elements.)
*
* To avoid double registrations, all registrations of the given delegate are first removed and then the delegate is added.
*
* @param {object} oDelegate the delegate object
* @param {boolean} [bCallBefore=false] if true, the delegate event listeners are called before the event listeners of the element; default is "false". In order to also set bClone, this parameter must be given.
* @param {object} [oThis] if given, this object will be the "this" context in the listener methods; default is the delegate object itself
* @param {boolean} [bClone=false] if true, this delegate will also be attached to any clones of this element; default is "false"
* @return {sap.ui.core.Element} Returns <code>this</code> to allow method chaining
* @private
*/
Element.prototype.addDelegate = function (oDelegate, bCallBefore, oThis, bClone) {
jQuery.sap.assert(oDelegate, "oDelegate must be not null or undefined");
if (!oDelegate) {
return this;
}
this.removeDelegate(oDelegate);
// shift parameters
if (typeof bCallBefore === "object") {
bClone = oThis;
oThis = bCallBefore;
bCallBefore = false;
}
if (typeof oThis === "boolean") {
bClone = oThis;
oThis = undefined;
}
(bCallBefore ? this.aBeforeDelegates : this.aDelegates).push({oDelegate:oDelegate, bClone: !!bClone, vThis: ((oThis === this) ? true : oThis)}); // special case: if this element is the given context, set a flag, so this also works after cloning (it should be the cloned element then, not the given one)
return this;
};
/**
* Removes the given delegate from this element.
*
* This method will remove all registrations of the given delegate, not only one.
* If the delegate was marked to be cloned and this element has been cloned, the delegate will not be removed from any clones.
*
* @param {object} oDelegate the delegate object
* @return {sap.ui.core.Element} Returns <code>this</code> to allow method chaining
* @private
*/
Element.prototype.removeDelegate = function (oDelegate) {
for (var i = 0;i < this.aDelegates.length;i++) {
if (this.aDelegates[i].oDelegate == oDelegate) {
this.aDelegates.splice(i,1);
}
}
for (var i = 0;i < this.aBeforeDelegates.length;i++) {
if (this.aBeforeDelegates[i].oDelegate == oDelegate) {
this.aBeforeDelegates.splice(i,1);
}
}
return this;
};
/**
* Adds a delegate that listens to the events that are fired on this element (as opposed to events which are fired BY this element).
*
* When this element is cloned, the same delegate will be added to all clones. This behavior is well-suited for applications which want to add delegates
* that also work with templates in aggregation bindings.
* For control development the internal "addDelegate" method which does not clone delegates by default may be more suitable, as typically each control instance takes care of its own delegates.
*
* To avoid double registrations, all registrations of the given delegate are first
* removed and then the delegate is added.
*
* @param {object} oDelegate the delegate object
* @param {object} [oThis] if given, this object will be the "this" context in the listener methods; default is the delegate object itself
* @return {sap.ui.core.Element} Returns <code>this</code> to allow method chaining
* @since 1.9.0
* @public
*/
Element.prototype.addEventDelegate = function (oDelegate, oThis) {
return this.addDelegate(oDelegate, false, oThis, true);
};
/**
* Removes the given delegate from this element.
*
* This method will remove all registrations of the given delegate, not only one.
*
* @param {object} oDelegate the delegate object
* @return {sap.ui.core.Element} Returns <code>this</code> to allow method chaining
* @since 1.9.0
* @public
*/
Element.prototype.removeEventDelegate = function (oDelegate) {
return this.removeDelegate(oDelegate);
};
/**
* Returns the DOM Element that should get the focus.
*
* To be overwritten by the specific control method.
*
* @return {Element} Returns the DOM Element that should get the focus
* @protected
*/
Element.prototype.getFocusDomRef = function () {
return this.getDomRef() || null;
};
/**
* Sets the focus to the stored focus DOM reference
* @public
*/
Element.prototype.focus = function () {
jQuery.sap.focus(this.getFocusDomRef());
};
/**
* Returns an object representing the serialized focus information
* To be overwritten by the specific control method
* @type object
* @return an object representing the serialized focus information
* @protected
*/
Element.prototype.getFocusInfo = function () {
return {id:this.getId()};
};
/**
* Applies the focus info.
*
* To be overwritten by the specific control method.
*
* @param {object} oFocusInfo
* @protected
*/
Element.prototype.applyFocusInfo = function (oFocusInfo) {
this.focus();
return this;
};
/**
* @see sap.ui.core.Element#setTooltip
* @private
*/
Element.prototype._refreshTooltipBaseDelegate = function (oTooltip) {
var oOldTooltip = this.getTooltip();
// if the old tooltip was a Tooltip object, remove it as a delegate
if (oOldTooltip instanceof sap.ui.core.TooltipBase) {
this.removeDelegate(oOldTooltip);
}
// if the new tooltip is a Tooltip object, add it as a delegate
if (oTooltip instanceof sap.ui.core.TooltipBase) {
oTooltip._currentControl = this;
this.addDelegate(oTooltip);
}
};
/**
* Sets a new tooltip for this object. The tooltip can either be a simple string
* (which in most cases will be rendered as the <code>title</code> attribute of this
* Element) or an instance of {@link sap.ui.core.TooltipBase}.
*
* If a new tooltip is set, any previously set tooltip is deactivated.
*
* @param {string|sap.ui.core.TooltipBase} oTooltip.
* @public
*/
Element.prototype.setTooltip = function(oTooltip) {
this._refreshTooltipBaseDelegate(oTooltip);
this.setAggregation("tooltip", oTooltip);
return this;
};
/**
* Returns the tooltip for this element if any or an undefined value.
* The tooltip can either be a simple string or a subclass of
* {@link sap.ui.core.TooltipBase}.
*
* Callers that are only interested in tooltips of type string (e.g. to render
* them as a <code>title</code> attribute), should call the convenience method
* {@link #getTooltip_AsString} instead. If they want to get a tooltip text no
* matter where it comes from (be it a string tooltip or the text from a TooltipBase
* instance) then they could call {@link #getTooltip_Text} instead.
*
* @return {string|sap.ui.core.TooltipBase} The tooltip for this Element.
* @public
*/
Element.prototype.getTooltip = function() {
return this.getAggregation("tooltip");
};
Element.runWithPreprocessors = ManagedObject.runWithPreprocessors;
/**
* Returns the tooltip for this element but only if it is a simple string.
* Otherwise an undefined value is returned.
*
* @return {string} string tooltip or undefined
* @public
*/
Element.prototype.getTooltip_AsString = function() {
var oTooltip = this.getTooltip();
if (typeof oTooltip === "string" || oTooltip instanceof String ) {
return oTooltip;
}
return undefined;
};
/**
* Returns the main text for the current tooltip or undefined if there is no such text.
* If the tooltip is an object derived from sap.ui.core.Tooltip, then the text property
* of that object is returned. Otherwise the object itself is returned (either a string
* or undefined or null).
*
* @return {string} text of the current tooltip or undefined
* @public
*/
Element.prototype.getTooltip_Text = function() {
var oTooltip = this.getTooltip();
if (oTooltip && typeof oTooltip.getText === "function" ) {
return oTooltip.getText();
}
return oTooltip;
};
/**
* Destroys the tooltip in the aggregation
* named <code>tooltip</code>.
* @return {sap.ui.core.Element} <code>this</code> to allow method chaining
* @public
* @name sap.ui.core.Element#destroyTooltip
* @function
*/
/**
* Returns the runtime metadata for this UI element.
*
* When using the defineClass method, this function is automatically created and returns
* a runtime representation of the design time metadata.
*
* @function
* @name sap.ui.core.Element.prototype.getMetadata
* @return {object} runtime metadata
* @public
*/
// sap.ui.core.Element.prototype.getMetadata = sap.ui.base.Object.ABSTRACT_METHOD;
//data container
(function(){
/**
* Returns the data object with the given key
*/
var getDataObject = function(element, key) {
var aData = element.getAggregation("customData");
if (aData) {
for (var i = 0; i < aData.length; i++) {
if (aData[i].getKey() == key) {
return aData[i];
}
}
}
return null;
};
/**
* Contains the data modification logic
*/
var setData = function(element, key, value, writeToDom) {
// DELETE
if (value === null) { // delete this property
var dataObject = getDataObject(element, key);
if (!dataObject) {
return;
}
var dataCount = element.getAggregation("customData").length;
if (dataCount == 1) {
element.destroyAggregation("customData", true); // destroy if there is no other data
} else {
element.removeAggregation("customData", dataObject, true);
dataObject.destroy();
}
// ADD or CHANGE
} else {
var dataObject = getDataObject(element, key);
if (dataObject) {
dataObject.setValue(value);
dataObject.setWriteToDom(writeToDom);
} else {
var dataObject = new sap.ui.core.CustomData({key:key,value:value, writeToDom:writeToDom});
element.addAggregation("customData", dataObject, true);
}
}
};
/**
* Attaches custom data to an Element or retrieves attached data.
*
* Usage:
* data("myKey", myData)
* attaches myData (which can be any JS data type, e.g. a number, a string, an object, or a function) to this element, under the given key "myKey". If the key already exists,the value will be updated.
*
* data("myKey", myData, writeToDom)
* attaches myData to this element, under the given key "myKey" and (if writeToDom is true) writes key and value to the HTML. If the key already exists,the value will be updated. While oValue can be any JS data type to be attached, it must be a string to be also written to DOM. The key must also be a valid HTML attribute name (it must conform to sap.ui.core.ID and may contain no colon) and may not start with "sap-ui". When written to HTML, the key is prefixed with "data-".
*
* data("myKey")
* retrieves whatever data has been attached to this Element (using the key "myKey") before
*
* data("myKey", null)
* removes whatever data has been attached to this Element (using the key "myKey") before
*
* data(null)
* removes all data
*
* data()
* returns all data, as a map
*
* @public
*/
Element.prototype.data = function() {
var argLength = arguments.length;
if (argLength == 0) { // return ALL data as a map
var aData = this.getAggregation("customData"),
result = {};
if (aData) {
for (var i = 0; i < aData.length; i++) {
result[aData[i].getKey()] = aData[i].getValue();
}
}
return result;
} else if (argLength == 1) {
var arg0 = arguments[0];
if (arg0 === null) { // delete ALL data
this.destroyAggregation("customData", true); // delete whole map
return this;
} else if (typeof arg0 == "string") { // return requested data element
var dataObject = getDataObject(this, arg0);
return dataObject ? dataObject.getValue() : null;
} else if (typeof arg0 == "object") { // should be a map - set multiple data elements
for (var key in arg0) { // TODO: improve performance and avoid executing setData multiple times
setData(this, key, arg0[key]);
}
return this;
} else {
// error, illegal argument
throw new Error("When data() is called with one argument, this argument must be a string, an object or null, but is " + (typeof arg0) + ":" + arg0 + " (on UI Element with ID '" + this.getId() + "')");
}
} else if (argLength == 2) { // set or remove one data element
setData(this, arguments[0], arguments[1]);
return this;
} else if (argLength == 3) { // set or remove one data element
setData(this, arguments[0], arguments[1], arguments[2]);
return this;
} else {
// error, illegal arguments
throw new Error("data() may only be called with 0-3 arguments (on UI Element with ID '" + this.getId() + "')");
}
};
})();
/**
* Clone delegates
* @param {string} [sIdSuffix] a suffix to be appended to the cloned element id
* @param {string[]} [aLocalIds] an array of local IDs within the cloned hierarchy (internally used)
* @return {sap.ui.base.ManagedObject} reference to the newly created clone
* @protected
*/
Element.prototype.clone = function(sIdSuffix, aLocalIds){
var oClone = ManagedObject.prototype.clone.apply(this, arguments);
// Clone delegates
for ( var i = 0; i < this.aDelegates.length; i++) {
if (this.aDelegates[i].bClone) {
oClone.aDelegates.push(this.aDelegates[i]);
}
}
for ( var i = 0; i < this.aBeforeDelegates.length; i++) {
if (this.aBeforeDelegates[i].bClone) {
oClone.aBeforeDelegates.push(this.aBeforeDelegates[i]);
}
}
return oClone;
};
/**
* Searches and returns an array of child elements and controls which are
* referenced within an aggregation or aggregations of child elements/controls.
* This can be either done recursive or not.
* <br>
* <b>Take care: this operation might be expensive.</b>
* @param {boolean}
* bRecursive true, if all nested children should be returned.
* @return {sap.ui.core.Element[]} array of child elements and controls
* @public
*/
Element.prototype.findElements = function(bRecursive) {
var aControls = ManagedObject.prototype.findAggregatedObjects.call(this, bRecursive);
return aControls;
};
/**
* Sets the {@link sap.ui.core.LayoutData} defining the layout constraints
* for this control when it is used inside a layout.
*
* @param {sap.ui.core.LayoutData} oLayoutData
* @public
*/
Element.prototype.setLayoutData = function(oLayoutData) {
this.setAggregation("layoutData", oLayoutData, true); // No invalidate because layout data changes does not affect the control / element itself
var oLayout = this.getParent();
if (oLayout) {
var oEvent = jQuery.Event("LayoutDataChange");
oEvent.srcControl = this;
oLayout._handleEvent(oEvent);
}
return this;
};
/**
* Allows the parent of a control to enhance the aria information during rendering
*
* This function is called by the RenderManager's writeAccessibilityState method
* for the parent of the currently rendered control - if the parent implements it.
*
* @function
* @name sap.ui.core.Element.prototype.enhanceAccessibilityState
* @param {sap.ui.core.Element} oElement the Control/Element for which aria properties are rendered
* @param {object} mAriaProps map of aria properties keyed by there name (withour prefix "aria-")
* @return {object} map of enhanced aria properties
* @protected
* @abstract
*/
/**
* Bind the object to the referenced entity in the model, which is used as the binding context
* to resolve bound properties or aggregations of the object itself and all of its children
* relatively to the given path.
* If a relative binding path is used, this will be applied whenever the parent context changes.
* @param {string} sPath the binding path
* @param {object} [mParameters] map of additional parameters for this binding
*
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
Element.prototype.bindElement = function(sPath, mParameters) {
return this.bindObject(sPath, mParameters);
};
/**
* Removes the defined binding context of this object, all bindings will now resolve
* relative to the parent context again.
*
* @param {string} sModelName
* @return {sap.ui.base.ManagedObject} reference to the instance itself
* @public
*/
Element.prototype.unbindElement = function(sModelName) {
return this.unbindObject(sModelName);
};
/**
* Get the element binding object for a specific model
*
* @param {string} sModelName the name of the model
* @return {sap.ui.model.Binding} the element binding for the given model name
* @public
*/
Element.prototype.getElementBinding = function(sModelName){
return this.getObjectBinding(sModelName);
};
return Element;
}, /* bExport= */ true);
},
"sap/ui/core/ElementMetadata.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*global Promise */
// Provides class sap.ui.core.ElementMetadata
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObjectMetadata'],
function(jQuery, ManagedObjectMetadata) {
"use strict";
/**
* Creates a new metadata object for a UIElement subclass.
*
* @param {string} sClassName fully qualified name of the class that is described by this metadata object
* @param {object} oStaticInfo static info to construct the metadata from
*
* @class
* @author SAP SE
* @version 1.28.5
* @since 0.8.6
* @alias sap.ui.core.ElementMetadata
*/
var ElementMetadata = function(sClassName, oClassInfo) {
// call super constructor
ManagedObjectMetadata.apply(this, arguments);
};
//chain the prototypes
ElementMetadata.prototype = jQuery.sap.newObject(ManagedObjectMetadata.prototype);
/**
* Calculates a new id based on a prefix.
*
* @return {string} A (hopefully unique) control id
* @public
* @function
*/
ElementMetadata.uid = ManagedObjectMetadata.uid;
/**
* By default, the element name is equal to the class name
* @return {string} the qualified name of the UIElement class
* @public
*/
ElementMetadata.prototype.getElementName = function() {
return this._sClassName;
};
/**
* Determines the class name of the renderer for the described control class.
*/
ElementMetadata.prototype.getRendererName = function() {
return this._sRendererName;
};
/**
* Retrieves the renderer for the described control class
*/
ElementMetadata.prototype.getRenderer = function() {
// determine name via function for those legacy controls that override getRendererName()
var sRendererName = this.getRendererName();
if ( !sRendererName ) {
return;
}
// check if renderer class exists already
var fnRendererClass = jQuery.sap.getObject(sRendererName);
if (fnRendererClass) {
return fnRendererClass;
}
// if not, try to load a module with the same name
jQuery.sap.require(sRendererName);
return jQuery.sap.getObject(sRendererName);
};
ElementMetadata.prototype.applySettings = function(oClassInfo) {
var oStaticInfo = oClassInfo.metadata;
this._sVisibility = oStaticInfo["visibility"] || "public";
// remove renderer stuff before calling super.
var vRenderer = oClassInfo.hasOwnProperty("renderer") ? (oClassInfo.renderer || "") : undefined;
delete oClassInfo.renderer;
ManagedObjectMetadata.prototype.applySettings.call(this, oClassInfo);
this._sRendererName = this.getName() + "Renderer";
if ( typeof vRenderer !== "undefined" ) {
if ( typeof vRenderer === "string" ) {
this._sRendererName = vRenderer || undefined;
return;
}
if ( typeof vRenderer === "function" ) {
vRenderer = { render : vRenderer };
}
var oParent = this.getParent();
var oBaseRenderer;
if ( oParent && oParent instanceof ElementMetadata ) {
oBaseRenderer = oParent.getRenderer();
}
if ( !oBaseRenderer ) {
jQuery.sap.require("sap.ui.core.Renderer");
oBaseRenderer = sap.ui.core.Renderer;
}
var oRenderer = jQuery.sap.newObject(oBaseRenderer);
jQuery.extend(oRenderer, vRenderer);
jQuery.sap.setObject(this.getRendererName(), oRenderer);
}
if (typeof oStaticInfo["designTime"] === "boolean") {
this._bHasDesignTime = oStaticInfo["designTime"];
} else if (oStaticInfo["designTime"]) {
this._bHasDesignTime = true;
this._oDesignTime = oStaticInfo["designTime"];
}
};
ElementMetadata.prototype.afterApplySettings = function() {
ManagedObjectMetadata.prototype.afterApplySettings.apply(this, arguments);
this.register && this.register(this);
};
ElementMetadata.prototype.isHidden = function() {
return this._sVisibility === "hidden";
};
ElementMetadata.prototype.loadDesignTime = function() {
var that = this;
return new Promise(function(fnResolve, fnReject) {
if (!that._oDesignTime && that._bHasDesignTime) {
var sModule = jQuery.sap.getResourceName(that.getElementName(), ".designtime");
sap.ui.require([sModule], function(oDesignTime) {
that._oDesignTime = oDesignTime;
fnResolve(oDesignTime);
});
} else {
fnResolve(that._oDesignTime);
}
});
};
return ElementMetadata;
}, /* bExport= */ true);
},
"sap/ui/core/FocusHandler.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.core.FocusHandler
sap.ui.define(['jquery.sap.global', 'sap/ui/Device', 'sap/ui/Global', 'sap/ui/base/Object', 'jquery.sap.script'],
function(jQuery, Device, Global, BaseObject/* , jQuerySap */) {
"use strict";
/**
* Constructs an instance of a sap.ui.core.FocusHandler.
* Keeps track of the focused element.
*
* @class Keeps track of the focused element.
* @param {Element} oRootRef e.g. document.body
* @param {sap.ui.core.Core} oCore Reference to the Core implementation
* @alias sap.ui.core.FocusHandler
*/
var FocusHandler = BaseObject.extend("sap.ui.core.FocusHandler", /** @lends sap.ui.core.FocusHandler.prototype */ {
constructor : function(oRootRef, oCore) {
BaseObject.apply(this);
this.oCore = oCore;
// keep track of element currently in focus
this.oCurrent = null;
// keep track of the element previously had the focus
this.oLast = null;
// buffer the focus/blur events for correct order
this.aEventQueue = [];
// keep track of last focused element
this.oLastFocusedControlInfo = null;
this.fEventHandler = jQuery.proxy(this.onEvent, this);
this.fDestroyHandler = jQuery.proxy(this.destroy, this);
// initialize event handling
if (oRootRef.addEventListener && !!!Device.browser.internet_explorer) { //FF, Safari
oRootRef.addEventListener("focus", this.fEventHandler, true);
oRootRef.addEventListener("blur", this.fEventHandler, true);
} else { //IE
jQuery(oRootRef).bind("activate", this.fEventHandler);
jQuery(oRootRef).bind("deactivate", this.fEventHandler);
}
jQuery.sap.log.debug("FocusHandler setup on Root " + oRootRef.type + (oRootRef.id ? ": " + oRootRef.id : ""), null, "sap.ui.core.FocusHandler");
// TODO: Or should we be destroyed by the Core?
jQuery(window).bind("unload", {"oRootRef": oRootRef}, this.fDestroyHandler);
}
});
/**
* Returns the Id of the control/element currently in focus.
* @return {string} the Id of the control/element currently in focus.
* @public
*/
FocusHandler.prototype.getCurrentFocusedControlId = function(){
var aCtrls = null;
try {
var $Act = jQuery(document.activeElement);
if ($Act.is(":focus") || (Device.browser.internet_explorer && Device.browser.version == 8 && document.hasFocus())) {
aCtrls = $Act.control();
}
} catch (err) {}
return aCtrls && aCtrls.length > 0 ? aCtrls[0].getId() : null;
};
/**
* Returns the focus info of the current focused control or the control with the given id, if exists.
*
* @see sap.ui.core.FocusHandler#restoreFocus
* @see sap.ui.core.FocusHandler#getCurrentFocusedControlId
* @param {string} [sControlId] the id of the control. If not given the id of the current focused control (if exists) is used
* @return {object} the focus info of the current focused control or the control with the given id, if exists.
* @private
*/
FocusHandler.prototype.getControlFocusInfo = function(sControlId){
sControlId = sControlId || this.getCurrentFocusedControlId();
if (!sControlId) {
return null;
}
var oControl = this.oCore.getElementById(sControlId);
if (oControl) {
return {
id : sControlId,
control : oControl,
info : oControl.getFocusInfo(),
type : oControl.getMetadata().getName(),
focusref : oControl.getFocusDomRef()
};
}
return null;
};
/**
* Restores the focus to the last known focused control or to the given focusInfo, if possible.
*
* @see sap.ui.core.FocusHandler#getControlFocusInfo
* @param {object} [oControlFocusInfo] the focus info previously received from getControlFocusInfo
* @private
*/
FocusHandler.prototype.restoreFocus = function(oControlFocusInfo){
var oInfo = oControlFocusInfo || this.oLastFocusedControlInfo;
if (!oInfo) {
return;
}
var oControl = this.oCore.getElementById(oInfo.id);
if (oControl && oInfo.info
&& oControl.getMetadata().getName() == oInfo.type
&& oControl.getFocusDomRef() != oInfo.focusref
&& (oControlFocusInfo || /*!oControlFocusInfo &&*/ oControl !== oInfo.control)) {
jQuery.sap.log.debug("Apply focus info of control " + oInfo.id, null, "sap.ui.core.FocusHandler");
oInfo.control = oControl;
this.oLastFocusedControlInfo = oInfo;
oControl.applyFocusInfo(oInfo.info);
} else {
jQuery.sap.log.debug("Apply focus info of control " + oInfo.id + " not possible", null, "sap.ui.core.FocusHandler");
}
};
/**
* Destroy method of the Focus Handler.
* It unregisters the event handlers.
*
* @param {jQuery.Event} event the event that initiated the destruction of the FocusHandler
* @private
*/
FocusHandler.prototype.destroy = function(event) {
var oRootRef = event.data.oRootRef;
if (oRootRef) {
if (oRootRef.removeEventListener && !!!Device.browser.internet_explorer) { //FF, Safari
oRootRef.removeEventListener("focus", this.fEventHandler, true);
oRootRef.removeEventListener("blur", this.fEventHandler, true);
} else { //IE
jQuery(oRootRef).unbind("activate", this.fEventHandler);
jQuery(oRootRef).unbind("deactivate", this.fEventHandler);
}
}
jQuery(window).unbind("unload", this.fDestroyHandler);
this.oCore = null;
};
/**
* Handles the focus/blur events.
*
* @param oRootRef e.g. document.body
* @private
*/
FocusHandler.prototype.onEvent = function(oBrowserEvent){
var oEvent = jQuery.event.fix(oBrowserEvent);
jQuery.sap.log.debug("Event " + oEvent.type + " reached Focus Handler (target: " + oEvent.target + (oEvent.target ? oEvent.target.id : "") + ")", null, "sap.ui.core.FocusHandler");
var type = (oEvent.type == "focus" || oEvent.type == "focusin" || oEvent.type == "activate") ? "focus" : "blur";
this.aEventQueue.push({type:type, controlId: getControlIdForDOM(oEvent.target)});
if (this.aEventQueue.length == 1) {
this.processEvent();
}
};
/**
* Processes the focus/blur events in the event queue.
*
* @private
*/
FocusHandler.prototype.processEvent = function(){
var oEvent = this.aEventQueue[0];
if (!oEvent) {
return;
}
try {
if (oEvent.type == "focus") {
this.onfocusEvent(oEvent.controlId);
} else if (oEvent.type == "blur") {
this.onblurEvent(oEvent.controlId);
}
} finally { //Ensure that queue is processed until it is empty!
this.aEventQueue.shift();
if (this.aEventQueue.length > 0) {
this.processEvent();
}
}
};
/**
* Processes the focus event taken from the event queue.
*
* @param sControlId Id of the event related control
* @private
*/
FocusHandler.prototype.onfocusEvent = function(sControlId){
var oControl = this.oCore.getElementById(sControlId);
if (oControl) {
this.oLastFocusedControlInfo = this.getControlFocusInfo(sControlId);
jQuery.sap.log.debug("Store focus info of control " + sControlId, null, "sap.ui.core.FocusHandler");
}
this.oCurrent = sControlId;
if (!this.oLast) {
// No last active element to be left...
return;
}
triggerFocusleave(this.oLast, sControlId, this.oCore);
this.oLast = null;
};
/**
* Processes the blur event taken from the event queue.
*
* @param sControlId Id of the event related control
* @private
*/
FocusHandler.prototype.onblurEvent = function(sControlId){
if (!this.oCurrent) {
// No current Item, so nothing to lose focus...
return;
}
this.oLast = sControlId;
this.oCurrent = null;
jQuery.sap.delayedCall(0, this, "checkForLostFocus");
};
/**
* Checks for lost focus and provides events in case of losing the focus.
* Called in delayed manner from {@link sap.ui.core.FocusHandler#onblurEvent}.
*
* @private
*/
FocusHandler.prototype.checkForLostFocus = function(){
if (this.oCurrent == null && this.oLast != null) {
triggerFocusleave(this.oLast, null, this.oCore);
}
this.oLast = null;
};
//***********************************************************
// Utility / convenience
//***********************************************************
/**
* Returns the id of the control/element to which the given DOM
* reference belongs to or <code>null</code> if no such
* control/element exists.
*
* @param {Element} oDOM the DOM reference
* @returns {string} Id of the control or null
* @private
*/
var getControlIdForDOM = function(oDOM){
var sId = jQuery(oDOM).closest("[data-sap-ui]").attr("id");
if (sId) {
return sId;
}
return null;
};
/**
* Calls the onsapfocusleave function on the control with id sControlId
* with the information about the given related control.
*
* @param {string} sControlId
* @param {string} sRelatedControlId
* @private
*/
var triggerFocusleave = function(sControlId, sRelatedControlId, oCore){
var oControl = sControlId ? sap.ui.getCore().byId(sControlId) : null;
if (oControl) {
var oRelatedControl = sRelatedControlId ? sap.ui.getCore().byId(sRelatedControlId) : null;
var oEvent = jQuery.Event("sapfocusleave");
oEvent.target = oControl.getDomRef();
oEvent.relatedControlId = oRelatedControl ? oRelatedControl.getId() : null;
oEvent.relatedControlFocusInfo = oRelatedControl ? oRelatedControl.getFocusInfo() : null;
//TODO: Cleanup the popup! The following is shit
var oControlUIArea = oControl.getUIArea();
var oUiArea = null;
if (oControlUIArea) {
oUiArea = oCore.getUIArea(oControlUIArea.getId());
} else {
var oPopupUIAreaDomRef = sap.ui.getCore().getStaticAreaRef();
if (jQuery.sap.containsOrEquals(oPopupUIAreaDomRef, oEvent.target)) {
oUiArea = oCore.getUIArea(oPopupUIAreaDomRef.id);
}
}
if (oUiArea) {
oUiArea._handleEvent(oEvent);
}
}
};
/*
* Checks if the passed DOM reference is nested in the active DOM of the document
* @param {Element} oDomRef The new active element
* @private
* @type boolean
* @returns {boolean} whether the passed DOM reference is nested in the active DOM of the document
*/
/*function isInActiveDom(oDomRef) {
jQuery.sap.assert(oDomRef != null);
var oCurrDomRef = oDomRef;
while(oCurrDomRef) {
if(oCurrDomRef === document) return true;
oCurrDomRef = oCurrDomRef.parentNode;
}
return false;
};*/
return FocusHandler;
}, /* bExport= */ true);
},
"sap/ui/core/LabelEnablement.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides helper sap.ui.core.LabelEnablement
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
// Mapping between controls and labels
var CONTROL_TO_LABELS_MAPPING = {};
// Returns the control for the given id (if available) and invalidates it if desired
function toControl(sId, bInvalidate) {
if (!sId) {
return null;
}
var oControl = sap.ui.getCore().byId(sId);
if (oControl && bInvalidate) {
oControl.invalidate();
}
return oControl;
}
// Updates the mapping tables for the given label, in destroy case only a cleanup is done
function refreshMapping(oLabel, bDestroy){
var sLabelId = oLabel.getId();
var sOldId = oLabel.__sLabeledControl;
var sNewId = bDestroy ? null : oLabel.getLabelForRendering();
if (sOldId == sNewId) {
return;
}
//Invalidate the label itself (see setLabelFor, setAlternativeLabelFor)
if (!bDestroy) {
oLabel.invalidate();
}
//Update the label to control mapping (1-1 mapping)
if (sNewId) {
oLabel.__sLabeledControl = sNewId;
} else {
delete oLabel.__sLabeledControl;
}
//Update the control to label mapping (1-n mapping)
var aLabelsOfControl;
if (sOldId) {
aLabelsOfControl = CONTROL_TO_LABELS_MAPPING[sOldId];
if (aLabelsOfControl) {
aLabelsOfControl = jQuery.grep(aLabelsOfControl, function(sCurrentLabelId) {
return sCurrentLabelId != sLabelId;
});
if (aLabelsOfControl.length) {
CONTROL_TO_LABELS_MAPPING[sOldId] = aLabelsOfControl;
} else {
delete CONTROL_TO_LABELS_MAPPING[sOldId];
}
}
}
if (sNewId) {
aLabelsOfControl = CONTROL_TO_LABELS_MAPPING[sNewId] || [];
aLabelsOfControl.push(sLabelId);
CONTROL_TO_LABELS_MAPPING[sNewId] = aLabelsOfControl;
}
//Invalidate related controls
toControl(sOldId, true);
toControl(sNewId, true);
}
// Checks whether enrich function can be applied on the given control or prototype.
function checkLabelEnablementPreconditions(oControl) {
if (!oControl) {
throw new Error("sap.ui.core.LabelEnablement cannot enrich null");
}
var oMetadata = oControl.getMetadata();
if (!oMetadata.isInstanceOf("sap.ui.core.Label")) {
throw new Error("sap.ui.core.LabelEnablement only supports Controls with interface sap.ui.core.Label");
}
var oLabelForAssociation = oMetadata.getAssociation("labelFor");
if (!oLabelForAssociation || oLabelForAssociation.multiple) {
throw new Error("sap.ui.core.LabelEnablement only supports Controls with a to-1 association 'labelFor'");
}
//Add more detailed checks here ?
}
/**
* Helper functionality for enhancement of a Label with common label functionality.
*
* @see sap.ui.core.LabelEnablement#enrich
*
* @author SAP SE
* @version 1.28.5
* @protected
* @alias sap.ui.core.LabelEnablement
* @namespace
*/
var LabelEnablement = {};
/**
* Helper function for the label control to render the html 'for' attribute. This function should be called
* at the desired location in the renderer code of the label control.
*
* @param {sap.ui.core.RenderManager} oRenderManager The RenderManager that can be used for writing to the render-output-buffer.
* @param {sap.ui.core.Label} oLabel The label for which the 'for' html attribute should be written to the render-output-buffer.
* @protected
*/
LabelEnablement.writeLabelForAttribute = function(oRenderManager, oLabel) {
if (!oLabel || !oLabel.getLabelForRendering) {
return;
}
var sControlId = oLabel.getLabelForRendering();
if (!sControlId) {
return;
}
var oControl = toControl(sControlId);
if (oControl && oControl.getIdForLabel) {
// for some controls the label must point to an special HTML element, not the outer one.
sControlId = oControl.getIdForLabel();
}
if (sControlId) {
oRenderManager.writeAttributeEscaped("for", sControlId);
}
};
/**
* Returns an array of ids of the labels referencing the given element
*
* @param {sap.ui.core.Element} oElement the element whose accessibility state should be rendered
* @returns {string[]} an array of ids of the labels referencing the given element
* @public
*/
LabelEnablement.getReferencingLabels = function(oElement){
var sId = oElement ? oElement.getId() : null;
if (!sId) {
return [];
}
return CONTROL_TO_LABELS_MAPPING[sId] || [];
};
/**
* This function should be called on a label control to enrich it's functionality.
*
* <b>Usage:</b>
* The function can be called with a control prototype:
* <code>
* sap.ui.core.LabelEnablement.enrich(my.Label.prototype);
* </code>
* Or the function can be called on instance level in the init function of a label control:
* <code>
* my.Label.prototype.init: function(){
* sap.ui.core.LabelEnablement.enrich(this);
* }
* </code>
*
* <b>Preconditions:</b>
* The given control must implement the interface sap.ui.core.Label and have an association 'labelFor' with cardinality 0..1.
* This function extends existing API functions. Ensure not to override this extensions AFTER calling this function.
*
* <b>What does this function do?</b>
*
* A mechanismn is added that ensures that a bidirectional reference between the label and it's labeled control is established:
* The label references the labeled control via the html 'for' attribute (@see sap.ui.core.LabelEnablement#writeLabelForAttribute).
* If the labeled control supports the aria-labelledby attribute. A reference to the label is added automatically.
*
* In addition an alternative to apply a for reference without influencing the labelFor association of the API is applied (e.g. used by Form).
* For this purpose the functions setAlternativeLabelFor and getLabelForRendering are added.
*
* @param {sap.ui.core.Control} oControl the label control which should be enriched with further label functionality.
* @throws Error if the given control cannot be enriched to violated preconditions (see above)
* @protected
*/
LabelEnablement.enrich = function(oControl) {
//Ensure that enhancement possible
checkLabelEnablementPreconditions(oControl);
oControl.__orig_setLabelFor = oControl.setLabelFor;
oControl.setLabelFor = function(sId) {
var res = this.__orig_setLabelFor.apply(this, arguments);
refreshMapping(this);
return res;
};
oControl.__orig_exit = oControl.exit;
oControl.exit = function() {
this._sAlternativeId = null;
refreshMapping(this, true);
if (oControl.__orig_exit) {
oControl.__orig_exit.apply(this, arguments);
}
};
// Alternative to apply a for reference without influencing the labelFor association of the API (see e.g. FormElement)
oControl.setAlternativeLabelFor = function(sId) {
if (sId instanceof sap.ui.base.ManagedObject) {
sId = sId.getId();
} else if (sId != null && typeof sId !== "string") {
jQuery.sap.assert(false, "setAlternativeLabelFor(): sId must be a string, an instance of sap.ui.base.ManagedObject or null");
return this;
}
this._sAlternativeId = sId;
refreshMapping(this);
return this;
};
// Returns id of the labelled control. The labelFor association is preferred before AlternativeLabelFor.
oControl.getLabelForRendering = function() {
return this.getLabelFor() || this._sAlternativeId;
};
};
return LabelEnablement;
}, /* bExport= */ true);
},
"sap/ui/core/Locale.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
//Provides the locale object sap.ui.core.Locale
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Object'],
function(jQuery, BaseObject) {
"use strict";
/**
* A regular expression that describes language tags according to BCP-47.
* @see BCP47 "Tags for Identifying Languages" (http://www.ietf.org/rfc/bcp/bcp47.txt)
*
* The matching groups are
* 0=all
* 1=language (shortest ISO639 code + ext. language sub tags | 4digits (reserved) | registered language sub tags)
* 2=script (4 letters)
* 3=region (2letter language or 3 digits)
* 4=variants (separated by '-', Note: capturing group contains leading '-' to shorten the regex!)
* 5=extensions (including leading singleton, multiple extensions separated by '-')
* 6=private use section (including leading 'x', multiple sections separated by '-')
*
* [-------------------- language ----------------------][--- script ---][------- region --------][------------ variants --------------][--------- extensions --------------][------ private use -------]
*/
var rLocale = /^((?:[A-Z]{2,3}(?:-[A-Z]{3}){0,3})|[A-Z]{4}|[A-Z]{5,8})(?:-([A-Z]{4}))?(?:-([A-Z]{2}|[0-9]{3}))?(-[0-9A-Z]{5,8}|(?:[0-9][0-9A-Z]{3}))*(?:-([0-9A-WYZ](?:-[0-9A-Z]{2,8})+))*(?:-(X(?:-[0-9A-Z]{1,8})+))?$/i;
/**
* Creates an instance of the Locale.
*
* @class Locale represents a locale setting, consisting of a language, script, region, variants, extensions and private use section
*
* @param {string} sLocaleId the locale identifier, in format en-US or en_US.
*
* @extends sap.ui.base.Object
* @author SAP SE
* @version 1.28.5
* @constructor
* @public
* @alias sap.ui.core.Locale
*/
var Locale = BaseObject.extend("sap.ui.core.Locale", /** @lends sap.ui.core.Locale.prototype */ {
constructor : function(sLocaleId) {
BaseObject.apply(this);
var aResult = rLocale.exec(sLocaleId.replace(/_/g, "-"));
// If the given Locale string cannot be parsed by the regular expression above we
// should at least tell the developer why the core fails to load.
if (aResult === null) {
throw "The given language does not adhere to BCP-47.";
}
this.sLocaleId = sLocaleId;
this.sLanguage = aResult[1] || null;
this.sScript = aResult[2] || null;
this.sRegion = aResult[3] || null;
this.sVariant = (aResult[4] && aResult[4].slice(1)) || null; // remove leading dash from capturing group
this.sExtension = aResult[5] || null;
this.sPrivateUse = aResult[6] || null;
// normalization according to BCP47:
if ( this.sLanguage ) {
this.sLanguage = this.sLanguage.toLowerCase();
}
if ( this.sScript ) {
this.sScript = this.sScript.toLowerCase().replace(/^[a-z]/, function($) {
return $.toUpperCase();
});
}
if ( this.sRegion ) {
this.sRegion = this.sRegion.toUpperCase();
}
},
/**
* Get the locale language.
*
* Note that the case might differ from the original script tag
* (Lower case is enforced as recommended by BCP47/ISO639).
*
* @return {string} the language code
* @public
*/
getLanguage : function() {
return this.sLanguage;
},
/**
* Get the locale script or null if none was specified.
*
* Note that the case might differ from the original language tag
* (Upper case first letter and lower case reminder enforced as
* recommended by BCP47/ISO15924)
*
* @return {string} the script code or null
* @public
*/
getScript : function() {
return this.sScript;
},
/**
* Get the locale region or null if none was specified.
*
* Note that the case might differ from the original script tag
* (Upper case is enforced as recommended by BCP47/ISO3166-1).
*
* @return {string} the ISO3166-1 region code (2-letter or 3-digits)
* @public
*/
getRegion : function() {
return this.sRegion;
},
/**
* Get the locale variants as a single string or null.
*
* Multiple variants are separated by a dash '-'.
*
* @return {string} the variant or null
* @public
*/
getVariant : function() {
return this.sVariant;
},
/**
* Get the locale variants as an array of individual variants.
*
* The separating dashes are not part of the result.
* If there is no variant section in the locale tag, an empty array is returned.
*
* @return {string[]} the individual variant sections
* @public
*/
getVariantSubtags : function() {
return this.sVariant ? this.sVariant.split('-') : [];
},
/**
* Get the locale extension as a single string or null.
*
* The extension always consists of a singleton character (not 'x'),
* a dash '-' and one or more extension token, each separated
* again with a dash.
*
* Use {@link #getExtensions} to get the individual extension tokens as an array.
*
* @return {string} the extension
* @public
*/
getExtension : function() {
return this.sExtension;
},
/**
* Get the locale extensions as an array of tokens.
*
* The leading singleton and the separating dashes are not part of the result.
* If there is no extensions section in the locale tag, an empty array is returned.
*
* @return {string[]} the individual extension sections
* @public
*/
getExtensionSubtags : function() {
return this.sExtension ? this.sExtension.slice(2).split('-') : [];
},
/**
* Get the locale private use section or null.
*
* @return {string} the private use section
* @public
*/
getPrivateUse : function() {
return this.sPrivateUse;
},
/**
* Get the locale private use section
*
* @return {string} the private use section
* @public
*/
getPrivateUseSubtags : function() {
return this.sPrivateUse ? this.sPrivateUse.slice(2).split('-') : [];
},
hasPrivateUseSubtag : function(sSubtag) {
jQuery.sap.assert(sSubtag && sSubtag.match(/^[0-9A-Z]{1,8}$/i), "subtag must be a valid BCP47 private use tag");
return jQuery.inArray(sSubtag, this.getPrivateUseSubtags()) >= 0;
},
toString : function() {
var r = [this.sLanguage];
if ( this.sScript ) {
r.push(this.sScript);
}
if ( this.sRegion ) {
r.push(this.sRegion);
}
if ( this.sVariant ) {
r.push(this.sVariant);
}
if ( this.sExtension ) {
r.push(this.sExtension );
}
if ( this.sPrivateUse ) {
r.push(this.sPrivateUse );
}
return r.join("-");
},
/**
* Best guess to get a proper SAP Logon Language for this locale.
*
* Conversions taken into account:
* <ul>
* <li>use the language part only</li>
* <li>convert old ISO639 codes to newer ones (e.g. 'iw' to 'he')</li>
* <li>for Chinese, map 'Traditional Chinese' to SAP proprietary code 'zf'</li>
* <li>map private extensions x-sap1q and x-sap2q to SAP pseudo languages '1Q' and '2Q'</li>
* <li>remove ext. language sub tags</li>
* <li>convert to uppercase</li>
* </ul>
*
* Note that the conversion also returns a result for languages that are not
* supported by the default set of SAP languages. This method has no knowledge
* about the concrete languages of any given backend system.
*
* @return {string} a language code that should
* @public
* @since 1.17.0
*/
getSAPLogonLanguage : function() {
var sLanguage = this.sLanguage || "",
m;
// cut off any ext. language sub tags
if ( sLanguage.indexOf("-") >= 0 ) {
sLanguage = sLanguage.slice(0, sLanguage.indexOf("-"));
}
// convert to new ISO codes
sLanguage = M_ISO639_OLD_TO_NEW[sLanguage] || sLanguage;
// handle special cases for Chinese: map 'Traditional Chinese' (or region TW which implies Traditional) to 'zf'
if ( sLanguage === "zh" ) {
if ( this.sScript === "Hant" || (!this.sScript && this.sRegion === "TW") ) {
sLanguage = "zf";
}
}
// recognize SAP supportability pseudo languages
if ( this.sPrivateUse && (m = /-(saptrc|sappsd)(?:-|$)/i.exec(this.sPrivateUse)) ) {
sLanguage = (m[1].toLowerCase() === "saptrc") ? "1Q" : "2Q";
}
// by convention, SAP systems seem to use uppercase letters
return sLanguage.toUpperCase();
}
});
var M_ISO639_OLD_TO_NEW = {
"iw" : "he",
"ji" : "yi",
"in" : "id",
"sh" : "sr"
};
/**
* Helper to analyze and parse designtime variables
*
* @private
*/
function getDesigntimePropertyAsArray(sValue) {
var m = /\$([-a-z0-9A-Z._]+)(?::([^$]*))?\$/.exec(sValue);
return (m && m[2]) ? m[2].split(/,/) : null;
}
/**
* A list of locales for which the CLDR specifies "right-to-left"
* as the character orientation.
*
* The string literal below is substituted during the build.
* The value is determined from the CLDR JSON files which are
* bundled with the UI5 runtime.
*/
var A_RTL_LOCALES = getDesigntimePropertyAsArray("$cldr-rtl-locales:ar,fa,he$") || [];
/**
* A list of locales for which CLDR data is bundled wit the UI5 runtime.
* @private
*/
Locale._cldrLocales = getDesigntimePropertyAsArray("$cldr-locales:ar,ar_EG,ar_SA,bg,br,ca,cs,da,de,de_AT,de_CH,el,el_CY,en,en_AU,en_GB,en_HK,en_IE,en_IN,en_NZ,en_PG,en_SG,en_ZA,es,es_AR,es_BO,es_CL,es_CO,es_MX,es_PE,es_UY,es_VE,et,fa,fi,fr,fr_BE,fr_CA,fr_CH,fr_LU,he,hi,hr,hu,id,it,it_CH,ja,ko,lt,lv,nb,nl,nl_BE,nn,pl,pt,pt_PT,ro,ru,ru_UA,sk,sl,sr,sv,th,tr,uk,vi,zh_CN,zh_HK,zh_SG,zh_TW$");
/**
* List of locales for which translated texts have been bundled with the UI5 runtime.
* @private
*/
Locale._coreI18nLocales = getDesigntimePropertyAsArray("$core-i18n-locales:,ar,bg,ca,cs,da,de,el,en,es,et,fi,fr,hi,hr,hu,it,iw,ja,ko,lt,lv,nl,no,pl,pt,ro,ru,sh,sk,sl,sv,th,tr,uk,vi,zh_CN,zh_TW$");
/**
* Checks whether the given language tag implies a character orientation
* of 'right-to-left' ('RTL').
*
* The implementation of this method and the configuration above assume
* that when a language (e.g. 'ar') is marked as 'RTL', then all language/region
* combinations for that language (e.g. 'ar_SA') will be 'RTL' as well,
* even if the combination is not mentioned in the above configuration.
* There is no mean to define RTL=false for a language/region, when RTL=true for
* the language alone.
*
* As of 3/2013 this is true for all supported locales/regions of UI5.
*
* @private
*/
Locale._impliesRTL = function(sLanguage) {
var oLocale = new Locale(sLanguage);
sLanguage = oLocale.getLanguage() || "";
sLanguage = (sLanguage && M_ISO639_OLD_TO_NEW[sLanguage]) || sLanguage;
var sRegion = oLocale.getRegion() || "";
if ( sRegion && jQuery.inArray(sLanguage + "_" + sRegion, A_RTL_LOCALES) >= 0 ) {
return true;
}
return jQuery.inArray(sLanguage, A_RTL_LOCALES) >= 0;
};
return Locale;
}, /* bExport= */ true);
},
"sap/ui/core/RenderManager.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the render manager sap.ui.core.RenderManager
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Interface', 'sap/ui/base/Object', 'sap/ui/core/LabelEnablement', 'jquery.sap.act', 'jquery.sap.encoder'],
function(jQuery, Interface, BaseObject, LabelEnablement /* , jQuerySap1, jQuerySap */) {
"use strict";
var aCommonMethods = ["renderControl", "write", "writeEscaped", "translate", "writeAcceleratorKey", "writeControlData",
"writeElementData", "writeAttribute", "writeAttributeEscaped", "addClass", "writeClasses",
"addStyle", "writeStyles", "writeAccessibilityState", "writeIcon",
"getConfiguration", "getHTML", "cleanupControlWithoutRendering"];
var aNonRendererMethods = ["render", "flush", "destroy"];
/**
* Creates an instance of the RenderManager.
*
* @class RenderManager that will take care for rendering Controls.
*
* The RenderManager will be available from the sap.ui.core.Core instance (available via <code>sap.ui.getCore()</code>).<br/>It
* can be used to render Controls and Control-Trees.
*
* The convention for renderers belonging to some controls is the following:
* <ul>
* <li>for a Control e.g. <code>sap.ui.controls.InputField</code> there shall be </li>
* <li>a renderer named <code>sap.ui.controls.InputFieldRenderer</code></li>
* <ul>
*
* @see sap.ui.core.Core
* @see sap.ui.getCore()
*
* @extends sap.ui.base.Object
* @author Jens Pflueger
* @version 1.28.5
* @constructor
* @alias sap.ui.core.RenderManager
* @public
*/
var RenderManager = BaseObject.extend("sap.ui.core.RenderManager", /** @lends sap.ui.core.RenderManager.prototype */ {
constructor : function() {
BaseObject.apply(this, arguments);
this.aBuffer = [];
this.aRenderedControls = [];
this.aStyleStack = [{}];
},
metadata : {
publicMethods : aCommonMethods.concat(aNonRendererMethods)
}
});
/**
* Returns the public interface of the RenderManager which can be used by Renderers.
*
* @return {sap.ui.base.Interface} the interface
* @private
*/
RenderManager.prototype.getRendererInterface = function() {
// see sap.ui.base.Object.getInterface for reference
var oInterface = new Interface(this, aCommonMethods);
this.getRendererInterface = jQuery.sap.getter(oInterface);
return oInterface;
};
/**
* Cleans up the resources associated with this instance.
* After the instance has been destroyed, it must not be used anymore.
* Applications should call this function if they don't need the instance any longer.
*
* @public
*/
RenderManager.prototype.destroy = function() {
this.aBuffer = [];
this.aRenderedControls = [];
this.aStyleStack = [{}];
};
/**
* Returns the configuration object
* Shortcut for <code>sap.ui.getCore().getConfiguration()</code>
* @return {sap.ui.core.Configuration} the configuration object
* @public
*/
RenderManager.prototype.getConfiguration = function() {
return sap.ui.getCore().getConfiguration();
};
/**
* Returns the renderer class for a given control instance
*
* @param {sap.ui.core.Control} oControl the control that should be rendered
* @return the renderer class for a given control instance
* @public
*/
RenderManager.prototype.getRenderer = function(oControl) {
jQuery.sap.assert(oControl && oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control");
return RenderManager.getRenderer(oControl);
};
/**
* Sets the focus handler to be used by the RenderManager.
*
* @param {sap.ui.core.FocusHandler} oFocusHandler the focus handler to be used.
* @private
*/
RenderManager.prototype._setFocusHandler = function(oFocusHandler) {
jQuery.sap.assert(oFocusHandler && oFocusHandler instanceof sap.ui.core.FocusHandler, "oFocusHandler must be a sap.ui.core.FocusHandler");
this.oFocusHandler = oFocusHandler;
};
//Triggers the BeforeRendering event on the given Control
var triggerBeforeRendering = function(oRM, oControl){
oRM._bLocked = true;
try {
var oEvent = jQuery.Event("BeforeRendering");
// store the element on the event (aligned with jQuery syntax)
oEvent.srcControl = oControl;
oControl._handleEvent(oEvent);
} finally {
oRM._bLocked = false;
}
};
/**
* Cleans up the rendering state of the given control with rendering it.
*
* A control is responsible for the rendering of all its child controls.
* But in some cases it makes sense that a control does not render all its
* children based on a filter condition. For example a Carousel control only renders
* the current visible parts (and maybe some parts before and after the visible area)
* for performance reasons.
* If a child was rendered but should not be rendered anymore because the filter condition
* does not apply anymore this child must be cleaned up correctly (e.g deregistering eventhandlers, ...).
*
* The following example shows how renderControl and cleanupControlWithoutRendering should
* be used:
*
* render = function(rm, ctrl){
* //...
* var aAggregatedControls = //...
* for(var i=0; i<aAgrregatedControls.length; i++){
* if(//... some filter expression){
* rm.renderControl(aAggregatedControls[i]);
* }else{
* rm.cleanupControlWithoutRendering(aAggregatedControls[i]);
* }
* }
* //...
* }
*
* Note:
* The method does not remove DOM of the given control. The callee of this method has to take over the
* responsibility to cleanup the DOM of the control afterwards.
* For parents which are rendered with the normal mechanism as shown in the example above this requirement
* is fulfilled, because the control is not added to the rendering buffer (renderControl is not called) and
* the DOM is replaced when the rendering cycle is finalized.
*
* @param {sap.ui.core.Control} oControl the control that should be cleaned up
* @public
* @since 1.22.9
*/
RenderManager.prototype.cleanupControlWithoutRendering = function(oControl) {
jQuery.sap.assert(!oControl || oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control or empty");
if (!oControl || !oControl.getDomRef()) {
return;
}
//Call beforeRendering to allow cleanup
triggerBeforeRendering(this, oControl);
oControl.bOutput = false;
};
/**
* Turns the given control into its HTML representation and appends it to the
* rendering buffer.
*
* If the given control is undefined or null, then nothing is rendered.
*
* @param {sap.ui.core.Control} oControl the control that should be rendered
* @public
*/
RenderManager.prototype.renderControl = function(oControl) {
jQuery.sap.assert(!oControl || oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control or empty");
// don't render a NOTHING
if (!oControl) {
return;
}
// create stack to determine rendered parent
if (!this.aRenderStack) {
this.aRenderStack = [];
}
// stop the measurement of parent
if (this.aRenderStack && this.aRenderStack.length > 0) {
jQuery.sap.measure.pause(this.aRenderStack[0] + "---renderControl");
} else if (oControl.getParent() && oControl.getParent().getMetadata().getName() == "sap.ui.core.UIArea") {
jQuery.sap.measure.pause(oControl.getParent().getId() + "---rerender");
}
this.aRenderStack.unshift(oControl.getId());
// start performance measurement
jQuery.sap.measure.start(oControl.getId() + "---renderControl","Rendering of " + oControl.getMetadata().getName());
//Remember the current buffer size to check later whether the control produced output
var iBufferLength = this.aBuffer.length;
var oControlStyles = {};
if (oControl.aCustomStyleClasses && oControl.aCustomStyleClasses.length > 0) {
oControlStyles.aCustomStyleClasses = oControl.aCustomStyleClasses; //cleared again in the writeClasses function
}
this.aStyleStack.push(oControlStyles);
jQuery.sap.measure.pause(oControl.getId() + "---renderControl");
// don't measure getRenderer because if Load needed its measured in Ajax call
// but start measurement before is to see general rendering time including loading time
// Either render the control normally, or invoke the InvisibleRenderer in case the control
// uses the default visible property
var oRenderer;
var oMetadata = oControl.getMetadata();
var bVisible = oControl.getVisible();
if (bVisible) {
// If the control is visible, return its renderer (Should be the default case, just like before)
oRenderer = oMetadata.getRenderer();
} else {
// If the control is invisible, find out whether it uses its own visible implementation
var oVisibleProperty = oMetadata.getProperty("visible");
var bUsesDefaultVisibleProperty =
oVisibleProperty
&& oVisibleProperty._oParent
&& oVisibleProperty._oParent.getName() == "sap.ui.core.Control";
oRenderer = bUsesDefaultVisibleProperty
// If the control inherited its visible property from sap.ui.core.Control, use
// the default InvisibleRenderer to render a placeholder instead of the real
// control HTML
? InvisibleRenderer
// If the control has their own visible property or one not inherited from
// sap.ui.core.Control, return the real renderer
: oMetadata.getRenderer();
}
jQuery.sap.measure.resume(oControl.getId() + "---renderControl");
triggerBeforeRendering(this, oControl);
// unbind any generically bound browser event handlers
var aBindings = oControl.aBindParameters;
if (aBindings && aBindings.length > 0) { // if we have stored bind calls...
var jDomRef = jQuery(oControl.getDomRef());
if (jDomRef && jDomRef[0]) { // ...and we have a DomRef
for (var i = 0; i < aBindings.length; i++) {
var oParams = aBindings[i];
jDomRef.unbind(oParams.sEventType, oParams.fnProxy);
}
}
}
//Render the control using the RenderManager interface
oRenderer.render(this.getRendererInterface(), oControl);
this.aStyleStack.pop();
//Remember the rendered control
this.aRenderedControls.push(oControl);
// let the UIArea know that this control has been rendered
// FIXME: RenderManager (RM) should not need to know about UIArea. Maybe UIArea should delegate rendering to RM
if ( oControl.getUIArea && oControl.getUIArea() ) {
oControl.getUIArea()._onControlRendered(oControl);
}
//Check whether the control has produced HTML
// Special case: If an invisible placeholder was rendered, use a non-boolean value
oControl.bOutput = this.aBuffer.length != iBufferLength;
if (oRenderer === InvisibleRenderer) {
oControl.bOutput = "invisible"; // Still evaluates to true, but can be checked for the special case
}
// end performance measurement
jQuery.sap.measure.end(oControl.getId() + "---renderControl");
this.aRenderStack.shift();
// resume the measurement of parent
if (this.aRenderStack && this.aRenderStack.length > 0) {
jQuery.sap.measure.resume(this.aRenderStack[0] + "---renderControl");
} else if (oControl.getParent() && oControl.getParent().getMetadata().getName() == "sap.ui.core.UIArea") {
jQuery.sap.measure.resume(oControl.getParent().getId() + "---rerender");
}
};
/**
* Renders the given {@link sap.ui.core.Control} and finally returns
* the content of the rendering buffer.
* Ensures the buffer is restored to the state before calling this method.
*
* @param {sap.ui.core.Control}
* oControl the Control whose HTML should be returned.
* @return {string} the resulting HTML of the provided control
* @deprecated Since version 0.15.0. Use <code>flush()</code> instead render content outside the rendering phase.
* @public
*/
RenderManager.prototype.getHTML = function(oControl) {
jQuery.sap.assert(oControl && oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control");
var tmp = this.aBuffer;
var aResult = this.aBuffer = [];
this.renderControl(oControl);
this.aBuffer = tmp;
return aResult.join("");
};
(function() {
//Does everything needed after the rendering (restore focus, calling "onAfterRendering", initialize event binding)
var finalizeRendering = function(oRM, aRenderedControls, oStoredFocusInfo){
var i, size = aRenderedControls.length;
for (i = 0; i < size; i++) {
aRenderedControls[i]._sapui_bInAfterRenderingPhase = true;
}
oRM._bLocked = true;
try {
// Notify the behavior object that the controls will be attached to DOM
for (i = 0; i < size; i++) {
var oControl = aRenderedControls[i];
if (oControl.bOutput && oControl.bOutput !== "invisible") {
var oEvent = jQuery.Event("AfterRendering");
// store the element on the event (aligned with jQuery syntax)
oEvent.srcControl = oControl;
// start performance measurement
jQuery.sap.measure.start(oControl.getId() + "---AfterRendering","AfterRendering of " + oControl.getMetadata().getName());
oControl._handleEvent(oEvent);
// end performance measurement
jQuery.sap.measure.end(oControl.getId() + "---AfterRendering");
}
}
} finally {
for (i = 0; i < size; i++) {
delete aRenderedControls[i]._sapui_bInAfterRenderingPhase;
}
oRM._bLocked = false;
}
//finally restore focus
try {
oRM.oFocusHandler.restoreFocus(oStoredFocusInfo);
} catch (e) {
jQuery.sap.log.warning("Problems while restore focus after rendering: " + e, null, oRM);
}
// Re-bind any generically bound browser event handlers (must happen after restoring focus to avoid focus event)
for (i = 0; i < size; i++) {
var oControl = aRenderedControls[i],
aBindings = oControl.aBindParameters;
if (aBindings && aBindings.length > 0) { // if we have stored bind calls...
var jDomRef = jQuery(oControl.getDomRef());
if (jDomRef && jDomRef[0]) { // ...and we have a DomRef - TODO: this check should not be required right after rendering...
for (var j = 0; j < aBindings.length; j++) {
var oParams = aBindings[j];
jDomRef.bind(oParams.sEventType, oParams.fnProxy);
}
}
}
}
};
/**
* Renders the content of the rendering buffer into the provided DOMNode.
*
* This function must not be called within control renderers.
*
* Usage:
* <pre>
* // Create a new instance of the RenderManager
* var rm = sap.ui.getCore().createRenderManager();
* // Use the writer API to fill the buffers
* rm.write(...);
* rm.renderControl(oControl);
* rm.write(...);
* ...
* // Finally flush the buffer into the provided DOM node (The current content is removed)
* rm.flush(oDomNode);
* // If the instance is not needed anymore, destroy it
* rm.destroy();
* </pre>
*
* @param {Element} oTargetDomNode The node in the dom where the buffer should be flushed into.
* @param {boolean} bDoNotPreserve flag, whether to not preserve (true) the content or to preserve it (false).
* @param {boolean|int} vInsert flag, whether to append (true) or replace (false) the buffer of the target dom node or to insert at a certain position (int)
* @public
*/
RenderManager.prototype.flush = function(oTargetDomNode, bDoNotPreserve, vInsert) {
jQuery.sap.assert((typeof oTargetDomNode === "object") && (oTargetDomNode.ownerDocument == document), "oTargetDomNode must be a DOM element");
if (this.bRendererMode) {
jQuery.sap.log.info("Flush must not be called from control renderers. Call ignored.", null, this);
return;
}
// preserve HTML content before flushing HTML into target DOM node
if (!bDoNotPreserve && (typeof vInsert !== "number") && !vInsert) { // expression mimics the conditions used below
RenderManager.preserveContent(oTargetDomNode);
}
var oStoredFocusInfo = this.oFocusHandler ? this.oFocusHandler.getControlFocusInfo() : null;
var sHTML = this.aBuffer.join("");
if (this._fPutIntoDom) {
//Case when render function was called
this._fPutIntoDom(oTargetDomNode, sHTML);
} else {
for (var i = 0; i < this.aRenderedControls.length; i++) {
//TODO It would be enough to loop over the controls for which renderControl was initially called but for this
//we have to manage an additional array. Rethink about later.
var oldDomNode = this.aRenderedControls[i].getDomRef();
if (oldDomNode && !RenderManager.isPreservedContent(oldDomNode)) {
if (RenderManager.isInlineTemplate(oldDomNode)) {
jQuery(oldDomNode).empty();
} else {
jQuery(oldDomNode).remove();
}
}
}
if (typeof vInsert === "number") {
if (vInsert <= 0) { // new HTML should be inserted at the beginning
jQuery(oTargetDomNode).prepend(sHTML);
} else { // new element should be inserted at a certain position > 0
var $predecessor = jQuery(oTargetDomNode).children().eq(vInsert - 1); // find the element which should be directly before the new one
if ($predecessor.length === 1) {
// element found - put the HTML in after this element
$predecessor.after(sHTML);
} else {
// element not found (this should not happen when properly used), append the new HTML
jQuery(oTargetDomNode).append(sHTML);
}
}
} else if (!vInsert) {
jQuery(oTargetDomNode).html(sHTML); // Put the HTML into the given DOM Node
} else {
jQuery(oTargetDomNode).append(sHTML); // Append the HTML into the given DOM Node
}
}
finalizeRendering(this, this.aRenderedControls, oStoredFocusInfo);
this.aRenderedControls = [];
this.aBuffer = [];
this.aStyleStack = [{}];
jQuery.sap.act.refresh();
};
/**
* Renders the given control to the provided DOMNode.
*
* If to control is already rendered in the provided DOMNode the DOM of the control is replaced. If the control
* is already rendered somewhere else the current DOM of the control is removed and the new DOM is appended
* to the provided DOMNode.
*
* This function must not be called within control renderers.
*
* @param {sap.ui.core.Control} oControl the Control that should be rendered.
* @param {Element} oTargetDomNode The node in the dom where the result of the rendering should be inserted.
* @public
*/
RenderManager.prototype.render = function(oControl, oTargetDomNode) {
jQuery.sap.assert(oControl && oControl instanceof sap.ui.core.Control, "oControl must be a control");
jQuery.sap.assert(typeof oTargetDomNode === "object" && oTargetDomNode.ownerDocument == document, "oTargetDomNode must be a DOM element");
if (this.bRendererMode) {
jQuery.sap.log.info("Render must not be called from control renderers. Call ignored.", null, this);
return;
}
if (this._bLocked) {
jQuery.sap.log.error("Render must not be called within Before or After Rendering Phase. Call ignored.", null, this);
return;
}
// Reset the buffer before rendering
this.aBuffer = [];
// Retrieve the markup (the rendering phase)
this.renderControl(oControl);
// FIXME: MULTIPLE ROOTS
// The implementation of this method doesn't support multiple roots for a control.
// Affects all places where 'oldDomNode' is used
this._fPutIntoDom = function(oTarget, sHTML){
if (oControl && oTargetDomNode) {
var oldDomNode = oControl.getDomRef();
if ( RenderManager.isPreservedContent(oldDomNode) ) {
// use placeholder instead
oldDomNode = jQuery.sap.byId(sap.ui.core.RenderPrefixes.Dummy + oControl.getId())[0] || oldDomNode;
}
if (!oldDomNode) {
// In case no old DOM node was found, search for the invisible placeholder
oldDomNode = jQuery.sap.domById(sap.ui.core.RenderPrefixes.Invisible + oControl.getId());
}
var bNewTarget = oldDomNode && oldDomNode.parentNode != oTargetDomNode;
var fAppend = function(){
var jTarget = jQuery(oTargetDomNode);
if (oTargetDomNode.innerHTML == "") {
jTarget.html(sHTML);
} else {
jTarget.append(sHTML);
}
};
if (bNewTarget) { //Control was rendered already and is now moved to different location
if (!RenderManager.isPreservedContent(oldDomNode)) {
if (RenderManager.isInlineTemplate(oldDomNode)) {
jQuery(oldDomNode).empty();
} else {
jQuery(oldDomNode).remove();
}
}
if (sHTML) {
fAppend();
}
} else { //Control either rendered initially or rerendered at the same location
if (sHTML) {
if (oldDomNode) {
if (RenderManager.isInlineTemplate(oldDomNode)) {
jQuery(oldDomNode).html(sHTML);
} else {
jQuery(oldDomNode).replaceWith(sHTML);
}
} else {
fAppend();
}
} else {
if (RenderManager.isInlineTemplate(oldDomNode)) {
jQuery(oldDomNode).empty();
} else {
// give parent control a chance to handle emptied children properly (e.g. XMLView)
if ( !oControl.getParent()
|| !oControl.getParent()._onChildRerenderedEmpty
|| !oControl.getParent()._onChildRerenderedEmpty(oControl, oldDomNode) ) {
jQuery(oldDomNode).remove();
}
}
}
}
}
};
this.flush(oTargetDomNode, true);
this._fPutIntoDom = null;
};
}());
//#################################################################################################
// Static Methods
//#################################################################################################
/**
* Returns the renderer class for a given control instance
*
* @param {sap.ui.core.Control}
* oControl the control that should be rendered
* @type function
* @return the renderer class for a given control instance
* @static
* @public
*/
RenderManager.getRenderer = function(oControl) {
jQuery.sap.assert(oControl && oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control");
return oControl.getMetadata().getRenderer();
};
/**
* Helper to enforce a repaint for a given dom node.
*
* Introduced to fix repaint issues in Webkit browsers, esp. Chrome.
* @param {Element} vDomNode a DOM node or ID of a DOM node
*
* @private
*/
RenderManager.forceRepaint = function(vDomNode) {
var oDomNode = typeof vDomNode == "string" ? jQuery.sap.domById(vDomNode) : vDomNode;
if ( oDomNode ) {
jQuery.sap.log.debug("forcing a repaint for " + (oDomNode.id || String(oDomNode)));
var sOriginalDisplay = oDomNode.style.display;
var oActiveElement = document.activeElement;
oDomNode.style.display = "none";
oDomNode.offsetHeight;
oDomNode.style.display = sOriginalDisplay;
if (document.activeElement !== oActiveElement) {
jQuery.sap.focus(oActiveElement);
}
}
};
//#################################################################################################
// Methods for preserving HTML content
//#################################################################################################
(function() {
var ID_PRESERVE_AREA = "sap-ui-preserve",
ID_STATIC_AREA = "sap-ui-static", // to be kept in sync with Core!
ATTR_PRESERVE_MARKER = "data-sap-ui-preserve",
ATTR_UI_AREA_MARKER = "data-sap-ui-area";
function getPreserveArea() {
var $preserve = jQuery("#" + ID_PRESERVE_AREA);
if ($preserve.length === 0) {
$preserve = jQuery("<DIV/>",{"aria-hidden":"true",id:ID_PRESERVE_AREA}).
addClass("sapUiHidden").addClass("sapUiForcedHidden").css("width", "0").css("height", "0").css("overflow", "hidden").
appendTo(document.body);
}
return $preserve;
}
/**
* Create a placeholder node for the given node (which must have an ID) and insert it before the node
*/
function makePlaceholder(node) {
jQuery("<DIV/>", { id: sap.ui.core.RenderPrefixes.Dummy + node.id}).addClass("sapUiHidden").insertBefore(node);
}
/**
* Collects descendants of the given root node that need to be preserved before the root node
* is wiped out. The "to-be-preserved" nodes are moved to a special, hidden 'preserve' area.
*
* A node is declared "to-be-preserved" when it has the <code>data-sap-ui-preserve</code>
* attribute set. When the optional parameter <code>bPreserveNodesWithId</code> is set to true,
* then nodes with an id are preserved as well and their <code>data-sap-ui-preserve</code> attribute
* is set automatically. This option is used by UIAreas when they render for the first time and
* simplifies the handling of predefined HTML content in a web page.
*
* The "to-be-preserved" nodes are searched with a depth first search and moved to the 'preserve'
* area in the order that they are found. So for direct siblings the order should be stable.
*
* @param {Element} oRootNode to search for "to-be-preserved" nodes
* @param {boolean} [bPreserveRoot=false] whether to preserve the root itself
* @param {boolean} [bPreserveNodesWithId=false] whether to preserve nodes with an id as well
* @public
* @static
*/
RenderManager.preserveContent = function(oRootNode, bPreserveRoot, bPreserveNodesWithId) {
jQuery.sap.assert(typeof oRootNode === "object" && oRootNode.ownerDocument == document, "oRootNode must be a DOM element");
sap.ui.getCore().getEventBus().publish("sap.ui","__preserveContent", { domNode : oRootNode});
var $preserve = getPreserveArea();
function check(candidate) {
// don't process the preserve area or the static area
if ( candidate.id === ID_PRESERVE_AREA || candidate.id === ID_STATIC_AREA ) {
return;
}
if ( candidate.hasAttribute(ATTR_PRESERVE_MARKER) ) { // node is marked with the preserve marker
// when the current node is the root node then we're doing a single control rerendering
if ( candidate === oRootNode ) {
makePlaceholder(candidate);
}
$preserve.append(candidate);
} else if ( bPreserveNodesWithId && candidate.id ) {
RenderManager.markPreservableContent(jQuery(candidate), candidate.id);
$preserve.append(candidate);
return;
}
// don't dive into nested UIAreas. They are preserved together with any preserved parent (e.g. HTML control)
if ( !candidate.hasAttribute(ATTR_UI_AREA_MARKER) ) {
var next = candidate.firstChild;
while ( next ) {
// determine nextSibiling before checking the candidate because
// a move to the preserveArea will modify the sibling relationship!
candidate = next;
next = next.nextSibling;
if ( candidate.nodeType === 1 /* Node.ELEMENT */ ) {
check(candidate);
}
}
}
}
jQuery.sap.measure.start(oRootNode.id + "---preserveContent","preserveContent for " + oRootNode.id);
if ( bPreserveRoot ) {
check(oRootNode);
} else {
jQuery(oRootNode).children().each(function(i,oNode) {
check(oNode);
});
}
jQuery.sap.measure.end(oRootNode.id + "---preserveContent");
};
/**
* Searches "to-be-preserved" nodes for the given control id.
*
* @param {string} sId control id to search content for.
* @return {jQuery} a jQuery collection representing the found content
* @public
* @static
*/
RenderManager.findPreservedContent = function(sId) {
jQuery.sap.assert(typeof sId === "string", "sId must be a string");
var $preserve = getPreserveArea(),
$content = $preserve.children("[" + ATTR_PRESERVE_MARKER + "='" + sId.replace(/(:|\.)/g,'\\$1') + "']");
return $content;
};
/**
* Marks the given content as "to-be-preserved" for a control with the given id.
* When later on the content has been preserved, it can be found by giving the same id.
*
* @param {jQuery} $content a jQuery collection of DOM objects to be marked
* @param {string} sId id of the control to associate the content with
* @static
*/
RenderManager.markPreservableContent = function($content, sId) {
$content.attr(ATTR_PRESERVE_MARKER, sId);
};
/**
* Checks whether the given DOM node is part of the 'preserve' area.
*
* @param {Element} oDomNode
* @return {boolean} whether node is part of 'preserve' area
* @private
* @static
*/
RenderManager.isPreservedContent = function(oDomNode) {
return ( oDomNode && oDomNode.getAttribute(ATTR_PRESERVE_MARKER) && oDomNode.parentNode && oDomNode.parentNode.id == ID_PRESERVE_AREA );
};
/**
* Returns the hidden area reference belonging to this window instance.
*
* @return {Element} the hidden area reference belonging to this core instance.
* @public
* @static
*/
RenderManager.getPreserveAreaRef = function() {
return getPreserveArea()[0];
};
var ATTR_INLINE_TEMPLATE_MARKER = "data-sap-ui-template";
/**
* Marks the given content as "inline template".
*
* @param {jQuery} $content a jQuery collection of DOM objects to be marked
* @private
* @static
*/
RenderManager.markInlineTemplate = function($content) {
$content.attr(ATTR_INLINE_TEMPLATE_MARKER, "");
};
/**
* Checks whether the given DOM node is an 'inline template' area.
*
* @param {Element} oDomNode
* @return {boolean} whether node is an 'inline template' area
* @private
* @static
*/
RenderManager.isInlineTemplate = function(oDomNode) {
return ( oDomNode && oDomNode.hasAttribute(ATTR_INLINE_TEMPLATE_MARKER) );
};
}());
//#################################################################################################
// Methods for 'Buffered writer' functionality... (all public)
// i.e. used methods in render-method of Renderers
//#################################################################################################
/**
* Write the given texts to the buffer
* @param {...string|number} sText (can be a number too)
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
* @SecSink {*|XSS}
*/
RenderManager.prototype.write = function(/** string|number */ sText /* ... */) {
jQuery.sap.assert(( typeof sText === "string") || ( typeof sText === "number"), "sText must be a string or number");
this.aBuffer.push.apply(this.aBuffer, arguments);
return this;
};
/**
* Escape text for HTML and write it to the buffer
* @param {string} sText
* @param {boolean} bLineBreaks Whether to convert linebreaks into <br> tags
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeEscaped = function(/** string */ sText, bLineBreaks) {
jQuery.sap.assert( typeof sText === "string", "sText must be a string");
sText = jQuery.sap.encodeHTML(sText);
if (bLineBreaks) {
sText = sText.replace(/&#xa;/g, "<br>");
}
this.aBuffer.push(sText);
return this;
};
/**
* @param {string} sKey
* @deprecated Not implemented - DO NOT USE
* @public
*/
RenderManager.prototype.translate = function(sKey) {
// TODO
};
/**
* @deprecated Not implemented - DO NOT USE
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeAcceleratorKey = function() {
/*
if (bAlt && !bCtrl && !bArrowKey) {
// Keyboard helper provides means for visualizing access keys.
// keydown modifies some CSS rule for showing underlines
// <span><u class="sapUiAccessKey">H</u>elp me</span>
UCF_KeyboardHelper.showAccessKeys();
}
*/
return this;
};
/**
* Adds a style property to the style collection if the value is not empty or null
* The style collection is flushed if it is written to the buffer using {@link #writeStyle}
*
* @param {string} sName name of the CSS property to write
* @param {string|float|int} value value to write
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
* @SecSink {0 1|XSS} Styles are written to HTML without validation
*/
RenderManager.prototype.addStyle = function(sName, value) {
jQuery.sap.assert(typeof sName === "string", "sName must be a string");
if (value !== undefined && value !== null) {
jQuery.sap.assert((typeof value === "string" || typeof value === "number"), "value must be a string or number");
var oStyle = this.aStyleStack[this.aStyleStack.length - 1];
if (!oStyle.aStyle) {
oStyle.aStyle = [];
}
oStyle.aStyle.push(sName + ":" + value);
}
return this;
};
/**
* Writes and flushes the style collection
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeStyles = function() {
var oStyle = this.aStyleStack[this.aStyleStack.length - 1];
if (oStyle.aStyle) {
this.write(" style=\"" + oStyle.aStyle.join(";") + "\" ");
}
oStyle.aStyle = null;
return this;
};
/**
* Adds a class to the class collection if the name is not empty or null.
* The class collection is flushed if it is written to the buffer using {@link #writeClasses}
*
* @param {string} sName name of the class to be added; null values are ignored
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
* @SecSink {0|XSS} Classes are written to HTML without validation
*/
RenderManager.prototype.addClass = function(sName) {
if (sName) {
jQuery.sap.assert(typeof sName === "string", "sName must be a string");
var oStyle = this.aStyleStack[this.aStyleStack.length - 1];
if (!oStyle.aClasses) {
oStyle.aClasses = [];
}
oStyle.aClasses.push(sName);
}
return this;
};
/**
* Writes and flushes the class collection (all CSS classes added by "addClass()" since the last flush).
* Also writes the custom style classes added by the application with "addStyleClass(...)". Custom classes are
* added by default from the currently rendered control. If an oElement is given, this Element's custom style
* classes are added instead. If oElement === false, no custom style classes are added.
*
* @param {sap.ui.core.Element | boolean} [oElement] an Element from which to add custom style classes (instead of adding from the control itself)
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeClasses = function(oElement) {
jQuery.sap.assert(!oElement || typeof oElement === "boolean" || oElement instanceof sap.ui.core.Element, "oElement must be empty, a boolean, or a sap.ui.core.Element");
var oStyle = this.aStyleStack[this.aStyleStack.length - 1];
// Custom classes are added by default from the currently rendered control. If an oElement is given, this Element's custom style
// classes are added instead. If oElement === false, no custom style classes are added.
var aCustomClasses;
if (oElement) {
aCustomClasses = oElement.aCustomStyleClasses;
} else if (oElement === false) {
aCustomClasses = [];
} else {
aCustomClasses = oStyle.aCustomStyleClasses;
}
if (oStyle.aClasses || aCustomClasses) {
var aClasses = [].concat(oStyle.aClasses || [], aCustomClasses || []);
aClasses.sort();
aClasses = jQuery.map(aClasses, function(n, i){
return (i == 0 || n != aClasses[i - 1]) ? n : null;
});
this.write(" class=\"", aClasses.join(" "), "\" ");
}
if (!oElement) {
oStyle.aCustomStyleClasses = null;
}
oStyle.aClasses = null;
return this;
};
/**
* Writes the controls data into the HTML.
* Control Data consists at least of the id of a control
* @param {sap.ui.core.Control} oControl the control whose identifying information should be written to the buffer
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeControlData = function(oControl) {
jQuery.sap.assert(oControl && oControl instanceof sap.ui.core.Control, "oControl must be a sap.ui.core.Control");
this.writeElementData(oControl);
return this;
};
/**
* Writes the elements data into the HTML.
* Element Data consists at least of the id of a element
* @param {sap.ui.core.Element} oElement the element whose identifying information should be written to the buffer
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeElementData = function(oElement) {
jQuery.sap.assert(oElement && oElement instanceof sap.ui.core.Element, "oElement must be a sap.ui.core.Element");
var sId = oElement.getId();
if (sId) {
this.writeAttribute("id", sId).writeAttribute("data-sap-ui", sId);
}
var aData = oElement.getCustomData();
var l = aData.length;
for (var i = 0; i < l; i++) {
var oCheckResult = aData[i]._checkWriteToDom(oElement);
if (oCheckResult) {
this.writeAttributeEscaped(oCheckResult.key, oCheckResult.value);
}
}
return this;
};
/**
* Writes the attribute and its value into the HTML
* @param {string} sName the name of the attribute
* @param {string | number | boolean} value the value of the attribute
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
* @SecSink {0 1|XSS} Attributes are written to HTML without validation
*/
RenderManager.prototype.writeAttribute = function(sName, value) {
jQuery.sap.assert(typeof sName === "string", "sName must be a string");
jQuery.sap.assert(typeof value === "string" || typeof value === "number" || typeof value === "boolean", "value must be a string, number or boolean");
this.write(" ", sName, "=\"", value, "\"");
return this;
};
/**
* Writes the attribute and its value into the HTML
*
* The value is properly escaped to avoid XSS attacks.
*
* @param {string} sName the name of the attribute
* @param {any} vValue the value of the attribute
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
* @SecSink {0|XSS}
*/
RenderManager.prototype.writeAttributeEscaped = function(sName, vValue) {
// writeAttribute asserts
this.writeAttribute(sName, jQuery.sap.escapeHTML(String(vValue)));
return this;
};
/**
* Writes the accessibility state (see WAI-ARIA specification) of the provided element into the HTML
* based on the element's properties and associations.
*
* The ARIA properties are only written when the accessibility feature is activated in the UI5 configuration.
*
* The following properties/values to ARIA attribute mappings are done (if the element does have such properties):
* <code>editable===false</code> => <code>aria-readonly="true"</code>
* <code>enabled===false</code> => <code>aria-disabled="true"</code>
* <code>visible===false</code> => <code>aria-hidden="true"</code>
* <code>required===true</code> => <code>aria-required="true"</code>
* <code>selected===true</code> => <code>aria-selected="true"</code>
* <code>checked===true</code> => <code>aria-checked="true"</code>
*
* Additionally the association <code>ariaDescribedBy</code> and <code>ariaLabelledBy</code> are used to write
* the id lists of the ARIA attributes <code>aria-describedby</code> and <code>aria-labelledby</code>.
*
* Note: This function is only a heuristic of a control property to ARIA attribute mapping. Control developers
* have to check whether it fullfills their requirements. In case of problems (for example the RadioButton has a
* <code>selected</code> property but must provide an <code>aria-checked</code> attribute) the auto-generated
* result of this function can be influenced via the parameter <code>mProps</code> as described below.
*
* The parameter <code>mProps</code> can be used to either provide additional attributes which should be added and/or
* to avoid the automatic generation of single ARIA attributes. The 'aria-' prefix will be prepended automatically to the keys
* (Exception: Attribute 'role' does not get the prefix 'aria-').
*
* Examples:
* <code>{hidden : true}</code> results in <code>aria-hidden="true"</code> independent of the precense or absence of
* the visibility property.
* <code>{hidden : null}</code> ensures that no <code>aria-hidden</code> attribute is written independent of the precense
* or absence of the visibility property.
* The function behaves in the same way for the associations <code>ariaDescribedBy</code> and <code>ariaLabelledBy</code>.
* To append additional values to the auto-generated <code>aria-describedby</code> and <code>aria-labelledby</code> attributes
* the following format can be used:
* <code>{describedby : {value: "id1 id2", append: true}}</code> => <code>aria-describedby="ida idb id1 id2"</code> (assuming that "ida idb"
* is the auto-generated part based on the association <code>ariaDescribedBy</code>).
*
* @param {sap.ui.core.Element}
* [oElement] the element whose accessibility state should be rendered
* @param {Object}
* [mProps] a map of properties that should be added additionally or changed.
* @return {sap.ui.core.RenderManager} this render manager instance to allow chaining
* @public
*/
RenderManager.prototype.writeAccessibilityState = function(oElement, mProps) {
if (!sap.ui.getCore().getConfiguration().getAccessibility()) {
return this;
}
if (arguments.length == 1 && !(oElement instanceof sap.ui.core.Element)) {
mProps = oElement;
oElement = null;
}
var mAriaProps = {};
if (oElement != null) {
var oMetadata = oElement.getMetadata();
var addACCForProp = function(sElemProp, sACCProp, oVal){
var oProp = oMetadata.getProperty(sElemProp);
if (oProp && oElement[oProp._sGetter]() === oVal) {
mAriaProps[sACCProp] = "true";
}
};
var addACCForAssoc = function(sElemAssoc, sACCProp){
var oAssoc = oMetadata.getAssociation(sElemAssoc);
if (oAssoc && oAssoc.multiple) {
var aIds = oElement[oAssoc._sGetter]();
if (sElemAssoc == "ariaLabelledBy") {
var aLabelIds = sap.ui.core.LabelEnablement.getReferencingLabels(oElement);
if (aLabelIds.length) {
aIds = aLabelIds.concat(aIds);
}
}
if (aIds.length > 0) {
mAriaProps[sACCProp] = aIds.join(" ");
}
}
};
addACCForProp("editable", "readonly", false);
addACCForProp("enabled", "disabled", false);
addACCForProp("visible", "hidden", false);
addACCForProp("required", "required", true);
addACCForProp("selected", "selected", true);
addACCForProp("checked", "checked", true);
addACCForAssoc("ariaDescribedBy", "describedby");
addACCForAssoc("ariaLabelledBy", "labelledby");
}
if (mProps) {
var checkValue = function(v){
var type = typeof (v);
return v === null || v === "" || type === "number" || type === "string" || type === "boolean";
};
var prop = {};
var x, val, autoVal;
for (x in mProps) {
val = mProps[x];
if (checkValue(val)) {
prop[x] = val;
} else if (typeof (val) === "object" && checkValue(val.value)) {
autoVal = "";
if (val.append && (x === "describedby" || x === "labelledby")) {
autoVal = mAriaProps[x] ? mAriaProps[x] + " " : "";
}
prop[x] = autoVal + val.value;
}
}
//The auto-generated values above can be overridden or reset (via null)
jQuery.extend(mAriaProps, prop);
}
// allow parent (e.g. FormElement) to overwrite or enhance aria attributes
if (oElement instanceof sap.ui.core.Element && oElement.getParent() && oElement.getParent().enhanceAccessibilityState) {
oElement.getParent().enhanceAccessibilityState(oElement, mAriaProps);
}
for (var p in mAriaProps) {
if (mAriaProps[p] != null && mAriaProps[p] !== "") { //allow 0 and false but no null, undefined or empty string
this.writeAttributeEscaped(p === "role" ? p : "aria-" + p, mAriaProps[p]);
}
}
return this;
};
/**
* Writes either an img tag for normal URI or an span tag with needed properties for icon URI.
*
* Additional classes and attributes can be added to the tag by given the second and third parameter.
* All of the given attributes are escaped for security consideration.
*
* when img tag is rendered, the following two attributes are added by default which can be overwritten by the provided mAttributes parameter:
* 1. role: presentation
* 2. alt: ""
*
* @param {sap.ui.core.URI} sURI is the URI of an image or an icon registered in sap.ui.core.IconPool.
* @param {array|string} aClasses are additional classes that are added to the rendered tag.
* @param {object} mAttributes are additional attributes that are added to the rendered tag.
* @returns {sap.ui.core.RenderManager} this render manager instance to allow chaining
*/
RenderManager.prototype.writeIcon = function(sURI, aClasses, mAttributes){
jQuery.sap.require("sap.ui.core.IconPool");
var bIconURI = sap.ui.core.IconPool.isIconURI(sURI),
sStartTag = bIconURI ? "<span " : "<img ",
sClasses, sProp, oIconInfo;
if (typeof aClasses === "string") {
aClasses = [aClasses];
}
if (bIconURI) {
oIconInfo = sap.ui.core.IconPool.getIconInfo(sURI);
if (!oIconInfo) {
jQuery.sap.log.error("An unregistered icon: " + sURI + " is used in sap.ui.core.RenderManager's writeIcon method.");
return this;
}
if (!aClasses) {
aClasses = [];
}
aClasses.push("sapUiIcon");
if (!oIconInfo.suppressMirroring) {
aClasses.push("sapUiIconMirrorInRTL");
}
}
this.write(sStartTag);
if (jQuery.isArray(aClasses) && aClasses.length) {
sClasses = aClasses.join(" ");
this.write("class=\"" + sClasses + "\" ");
}
if (bIconURI) {
if (!mAttributes) {
mAttributes = {};
}
mAttributes["data-sap-ui-icon-content"] = oIconInfo.content;
mAttributes["role"] = "img";
mAttributes["aria-label"] = oIconInfo.name;
this.write("style=\"font-family: " + oIconInfo.fontFamily + ";\" ");
} else {
mAttributes = jQuery.extend({
role: "presentation",
alt: "",
src: sURI
}, mAttributes);
}
if (typeof mAttributes === "object") {
for (sProp in mAttributes) {
if (mAttributes.hasOwnProperty(sProp)) {
this.writeAttributeEscaped(sProp, mAttributes[sProp]);
}
}
}
this.write(bIconURI ? "></span>" : "/>");
return this;
};
/**
* Renders an invisible dummy element for controls that have set their visible-property to
* false. In case the control has its own visible property, it has to handle rendering itself.
*/
var InvisibleRenderer = {
/**
* Renders the invisible dummy element
*
* @param {sap.ui.core.RenderManager} [oRm] The RenderManager instance
* @param {sap.ui.core.Control} [oControl] The instance of the invisible control
*/
render: function(oRm, oControl) {
var sPlaceholderId = sap.ui.core.RenderPrefixes.Invisible + oControl.getId();
var sPlaceholderHtml =
'<span ' +
'id="' + sPlaceholderId + '" ' +
'class="sapUiHiddenPlaceholder" ' +
'data-sap-ui="' + sPlaceholderId + '" ' +
'style="display: none;"' +
'aria-hidden="true">' +
'</span>';
oRm.write(sPlaceholderHtml);
}
};
return RenderManager;
}, /* bExport= */ true);
},
"sap/ui/core/ResizeHandler.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.core.ResizeHandler
sap.ui.define(['jquery.sap.global', 'sap/ui/Global', 'sap/ui/base/Object', 'jquery.sap.act', 'jquery.sap.script'],
function(jQuery, Global, BaseObject/* , jQuerySap1, jQuerySap */) {
"use strict";
// local logger, by default only logging errors
var log = jQuery.sap.log.getLogger("sap.ui.core.ResizeHandler", jQuery.sap.log.Level.ERROR);
/**
* Reference to the Core (implementation view, not facade)
* @type {sap.ui.core.Core}
*/
var oCoreRef = null;
/**
* API for resize handling on registered DOM elements and controls.
*
* This API provides firing of resize events on all browsers by regularly
* checking width and height of registered DOM elements and controls and firing events accordingly.
*
* @namespace
* @alias sap.ui.core.ResizeHandler
* @public
*/
var ResizeHandler = BaseObject.extend("sap.ui.core.ResizeHandler", /** @lends sap.ui.core.ResizeHandler.prototype */ {
constructor : function(oCore) {
BaseObject.apply(this);
oCoreRef = oCore;
this.aResizeListeners = [];
this.bRegistered = false;
this.iIdCounter = 0;
this.fDestroyHandler = jQuery.proxy(this.destroy, this);
jQuery(window).bind("unload", this.fDestroyHandler);
jQuery.sap.act.attachActivate(initListener, this);
}
});
function clearListener(){
if (this.bRegistered) {
this.bRegistered = false;
sap.ui.getCore().detachIntervalTimer(this.checkSizes, this);
}
}
function initListener(){
if (!this.bRegistered && this.aResizeListeners.length > 0) {
this.bRegistered = true;
sap.ui.getCore().attachIntervalTimer(this.checkSizes, this);
}
}
/**
* Destroy method of the Resize Handler.
* It unregisters the event handlers.
*
* @param {jQuery.Event} oEvent the event that initiated the destruction of the ResizeHandler
* @private
*/
ResizeHandler.prototype.destroy = function(oEvent) {
jQuery.sap.act.detachActivate(initListener, this);
jQuery(window).unbind("unload", this.fDestroyHandler);
oCoreRef = null;
this.aResizeListeners = [];
clearListener.apply(this);
};
/**
* Attaches listener to resize event.
*
* @param {Element|sap.ui.core.Control} oRef the DOM reference or a control
* @param {function} fHandler the event handler function
* @return {string} Registration-ID for later detaching.
* @private
*/
ResizeHandler.prototype.attachListener = function(oRef, fHandler){
var bIsControl = oRef instanceof sap.ui.core.Control,
oDom = bIsControl ? oRef.getDomRef() : oRef,
iWidth = oDom ? oDom.offsetWidth : 0,
iHeight = oDom ? oDom.offsetHeight : 0,
sId = "rs-" + new Date().valueOf() + "-" + this.iIdCounter++,
dbg;
if (bIsControl) {
dbg = ("Control " + oRef.getId());
} else if (oRef.id) {
dbg = oRef.id;
} else {
dbg = String(oRef);
}
this.aResizeListeners.push({sId: sId, oDomRef: bIsControl ? null : oRef, oControl: bIsControl ? oRef : null, fHandler: fHandler, iWidth: iWidth, iHeight: iHeight, dbg: dbg});
log.debug("registered " + dbg);
initListener.apply(this);
return sId;
};
/**
* Detaches listener from resize event.
*
* @param {string} Registration-ID returned from attachListener
* @private
*/
ResizeHandler.prototype.detachListener = function(sId){
var that = this;
jQuery.each(this.aResizeListeners, function(index, oResizeListener){
if (oResizeListener.sId == sId) {
that.aResizeListeners.splice(index,1);
log.debug("deregistered " + sId);
return false; //break the loop
}
});
// if list is empty now, stop interval
if (this.aResizeListeners.length == 0) {
clearListener.apply(this);
}
};
/**
* Check sizes of resize elements.
* @private
*/
ResizeHandler.prototype.checkSizes = function() {
var bDebug = log.isLoggable();
if ( bDebug ) {
log.debug("checkSizes:");
}
jQuery.each(this.aResizeListeners, function(index, oResizeListener){
if (oResizeListener) {
var bCtrl = !!oResizeListener.oControl,
oDomRef = bCtrl ? oResizeListener.oControl.getDomRef() : oResizeListener.oDomRef;
if ( oDomRef && jQuery.contains(document.documentElement, oDomRef)) { //check that domref is still active
var iOldWidth = oResizeListener.iWidth,
iOldHeight = oResizeListener.iHeight,
iNewWidth = oDomRef.offsetWidth,
iNewHeight = oDomRef.offsetHeight;
if (iOldWidth != iNewWidth || iOldHeight != iNewHeight) {
oResizeListener.iWidth = iNewWidth;
oResizeListener.iHeight = iNewHeight;
var oEvent = jQuery.Event("resize");
oEvent.target = oDomRef;
oEvent.currentTarget = oDomRef;
oEvent.size = {width: iNewWidth, height: iNewHeight};
oEvent.oldSize = {width: iOldWidth, height: iOldHeight};
oEvent.control = bCtrl ? oResizeListener.oControl : null;
if ( bDebug ) {
log.debug("resize detected for '" + oResizeListener.dbg + "': " + oEvent.oldSize.width + "x" + oEvent.oldSize.height + " -> " + oEvent.size.width + "x" + oEvent.size.height);
}
oResizeListener.fHandler(oEvent);
}
}
}
});
if (ResizeHandler._keepActive != true && ResizeHandler._keepActive != false) {
//initialize default
ResizeHandler._keepActive = false;
}
if (!jQuery.sap.act.isActive() && !ResizeHandler._keepActive) {
clearListener.apply(this);
}
};
/**
* Registers the given handler for resize events on the given
* DOM reference or Control.
* In case the core is not initialized yet, the timer cannot be registered and this method
* will return null. Please use sap.ui.getCore().attachInit() with a callback as parameter
* that calls ResizeHandler.register().
*
* @param {Element|sap.ui.core.Control} oRef the Control or the DOM reference for which the given handler should be registered (beside the window)
* @param {function} fHandler the handler which should be called on a resize event
* @return {string} Registration ID which can be used for deregistering
* @public
*/
ResizeHandler.register = function(oRef, fHandler) {
if (!oCoreRef || !oCoreRef.oResizeHandler) {
return null;
}
return oCoreRef.oResizeHandler.attachListener(oRef, fHandler);
};
/**
* Deregisters the registered handler for resize events with the given ID.
*
* @param {string} sId Registration ID
* @public
*/
ResizeHandler.deregister = function(sId) {
if (!oCoreRef || !oCoreRef.oResizeHandler) {
return;
}
oCoreRef.oResizeHandler.detachListener(sId);
};
/**
* Deregisters all registered handler for resize events for the given control.
*
* @param {string} sControlId the control Id
* @private
*/
ResizeHandler.deregisterAllForControl = function(sControlId) {
if (!oCoreRef || !oCoreRef.oResizeHandler) {
return;
}
var aIds = [];
jQuery.each(oCoreRef.oResizeHandler.aResizeListeners, function(index, oResizeListener){
if (oResizeListener && oResizeListener.oControl && oResizeListener.oControl.getId() === sControlId) {
aIds.push(oResizeListener.sId);
}
});
jQuery.each(aIds, function(index, sId){
ResizeHandler.deregister(sId);
});
};
return ResizeHandler;
}, /* bExport= */ true);
},
"sap/ui/core/ThemeCheck.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*global URI*/// declare unusual global vars for JSLint/SAPUI5 validation
// Provides class sap.ui.core.ThemeCheck
sap.ui.define(['jquery.sap.global', 'sap/ui/Device', 'sap/ui/base/Object', 'jquery.sap.script'],
function(jQuery, Device, BaseObject/* , jQuerySap */) {
"use strict";
sap.ui._maxThemeCheckCycles = 100;
/**
* Creates a new ThemeCheck object.
*
* @class Helper class used by the UI5 Core to check whether the themes are applied correctly.
*
* It could happen that e.g. in onAfterRendering not all themes are available. In these cases the
* check waits until the CSS is applied and fires an onThemeChanged event.
*
* @extends sap.ui.base.Object
* @since 1.10.0
* @author SAP SE
* @constructor
* @private
* @alias sap.ui.core.ThemeCheck
*/
var ThemeCheck = BaseObject.extend("sap.ui.core.ThemeCheck", /** @lends sap.ui.core.ThemeCheck.prototype */ {
constructor : function(oCore) {
this._oCore = oCore;
this._iCount = 0; // Prevent endless loop
this._CUSTOMCSSCHECK = /\.sapUiThemeDesignerCustomCss/i;
this._CUSTOMID = "sap-ui-core-customcss";
this._customCSSAdded = false;
this._themeCheckedForCustom = null;
this._mAdditionalLibCss = {};
},
getInterface : function() {
return this;
},
fireThemeChangedEvent : function(bOnlyOnInitFail, bForceCheck) {
clear(this);
var bUseThemeCheck = sap.ui._maxThemeCheckCycles > 0; //Possibility to switch off theme check (except of force mode (for Core.applyTheme))
if (bUseThemeCheck || bForceCheck) {
delayedCheckTheme.apply(this, [true]);
} else {
ThemeCheck.themeLoaded = true;
}
if (!bOnlyOnInitFail && !this._sThemeCheckId) {
this._oCore.fireThemeChanged({theme: this._oCore.getConfiguration().getTheme()});
}
}
});
ThemeCheck.themeLoaded = false;
ThemeCheck.checkStyle = function(oStyle, bLog){
if (typeof (oStyle) === "string") {
oStyle = jQuery.sap.domById(oStyle);
}
var $Style = jQuery(oStyle);
try {
var res = !oStyle || !!((oStyle.sheet && oStyle.sheet.cssRules.length > 0) ||
!!(oStyle.styleSheet && oStyle.styleSheet.cssText.length > 0) ||
!!(oStyle.innerHTML && oStyle.innerHTML.length > 0));
var res2 = $Style.attr("sap-ui-ready");
res2 = !!(res2 === "true" || res2 === "false");
if (bLog) {
jQuery.sap.log.debug("ThemeCheck: Check styles '" + $Style.attr("id") + "': " + res + "/" + res2 + "/" + !!oStyle);
}
return res || res2;
} catch (e) {}
if (bLog) {
jQuery.sap.log.debug("ThemeCheck: Error during check styles '" + $Style.attr("id") + "': false/false/" + !!oStyle);
}
return false;
};
function clear(oThemeCheck){
ThemeCheck.themeLoaded = false;
if (oThemeCheck._sThemeCheckId) {
jQuery.sap.clearDelayedCall(oThemeCheck._sThemeCheckId);
oThemeCheck._sThemeCheckId = null;
oThemeCheck._iCount = 0;
oThemeCheck._mAdditionalLibCss = {};
}
}
function checkTheme(oThemeCheck) {
var mLibs = oThemeCheck._oCore.getLoadedLibraries();
var sThemeName = oThemeCheck._oCore.getConfiguration().getTheme();
var sPath = oThemeCheck._oCore._getThemePath("sap.ui.core", sThemeName) + "custom.css";
var res = true;
if (!!oThemeCheck._customCSSAdded && oThemeCheck._themeCheckedForCustom === sThemeName) {
// include custom style sheet here because it has already been added using jQuery.sap.includeStyleSheet
// hence, needs to be checked for successful inclusion, too
mLibs[oThemeCheck._CUSTOMID] = {};
}
function checkLib(lib) {
res = res && ThemeCheck.checkStyle("sap-ui-theme-" + lib, true);
if (!!res) {
// check for css rule count
if (Device.browser.msie && Device.browser.version <= 9) {
var oStyle = jQuery.sap.domById("sap-ui-theme-" + lib);
var iRules = oStyle && oStyle.sheet && oStyle.sheet.rules &&
oStyle.sheet.rules.length ? oStyle.sheet.rules.length : 0;
// IE9 and below can only handle up to 4095 rules and therefore additional
// css files have to be included
if (iRules === 4095) {
var iNumber = parseInt(jQuery(oStyle).attr("sap-ui-css-count"), 10);
if (isNaN(iNumber)) {
iNumber = 1; // first additional stylesheet
} else {
iNumber += 1;
}
var sAdditionalLibSuffix = "ie9_" + iNumber;
var sAdditionalLibName = this.name + "-" + sAdditionalLibSuffix;
var sLinkId = "sap-ui-theme-" + sAdditionalLibName;
if (!oThemeCheck._mAdditionalLibCss[sAdditionalLibName] && !jQuery.sap.domById(sLinkId)) {
oThemeCheck._mAdditionalLibCss[sAdditionalLibName] = {
name: this.name // remember original libName
};
var oBaseStyleSheet;
if (lib !== this.name) {
// use first stylesheet element of theme
oBaseStyleSheet = jQuery.sap.domById("sap-ui-theme-" + this.name);
} else {
oBaseStyleSheet = oStyle;
}
// parse original href
var oHref = new URI(oBaseStyleSheet.getAttribute("href"));
var sSuffix = oHref.suffix();
// get filename without suffix
var sFileName = oHref.filename();
if (sSuffix.length > 0) {
sSuffix = "." + sSuffix;
sFileName = sFileName.slice(0, -sSuffix.length);
}
// change filename only (to keep URI parameters)
oHref.filename(sFileName + "_" + sAdditionalLibSuffix + sSuffix);
// build final href
var sHref = oHref.toString();
// create the new link element
var oLink = document.createElement("link");
oLink.type = "text/css";
oLink.rel = "stylesheet";
oLink.href = sHref;
oLink.id = sLinkId;
jQuery(oLink)
.attr("sap-ui-css-count", iNumber)
.load(function() {
jQuery(oLink).attr("sap-ui-ready", "true");
}).error(function() {
jQuery(oLink).attr("sap-ui-ready", "false");
});
oStyle.parentNode.insertBefore(oLink, oStyle.nextSibling);
}
}
}
/* as soon as css has been loaded, look if there is a flag for custom css inclusion inside, but only
* if this has not been checked successfully before for the same theme
*/
if (oThemeCheck._themeCheckedForCustom != sThemeName) {
if (checkCustom(oThemeCheck, lib)) {
//load custom css available at sap/ui/core/themename/library.css
jQuery.sap.includeStyleSheet(sPath, oThemeCheck._CUSTOMID);
oThemeCheck._customCSSAdded = true;
jQuery.sap.log.warning("ThemeCheck delivered custom CSS needs to be loaded, Theme not yet applied");
oThemeCheck._themeCheckedForCustom = sThemeName;
res = false;
return false;
} else {
// remove stylesheet once the particular class is not available (e.g. after theme switch)
/*check for custom theme was not successful, so we need to make sure there are no custom style sheets attached*/
var customCssLink = jQuery("LINK[id='" + oThemeCheck._CUSTOMID + "']");
if (customCssLink.length > 0) {
customCssLink.remove();
jQuery.sap.log.debug("Custom CSS removed");
}
oThemeCheck._customCSSAdded = false;
}
}
}
}
jQuery.each(mLibs, checkLib);
jQuery.each(oThemeCheck._mAdditionalLibCss, checkLib);
if (!res) {
jQuery.sap.log.warning("ThemeCheck: Theme not yet applied.");
} else {
oThemeCheck._themeCheckedForCustom = sThemeName;
}
return res;
}
/* checks if a particular class is available at the beginning of the core styles
*/
function checkCustom (oThemeCheck, lib){
var iRulesToCheck = 2,
bSuccess = false,
aRules = [];
if (jQuery.sap.domById("sap-ui-theme-" + lib)) {
var cssFile = jQuery.sap.domById("sap-ui-theme-" + lib);
if (cssFile.sheet) {
aRules = cssFile.sheet.cssRules;
} else if (cssFile.styleSheet) {
//we're in an old IE version
aRules = cssFile.styleSheet.rules;
}
}
if (aRules.length == 0) {
jQuery.sap.log.warning("Custom check: Failed retrieving a CSS rule from stylesheet " + lib);
return false;
}
// we should now have some rule name ==> try to match against custom check
for (var i = 0; (i < iRulesToCheck && i < aRules.length) ; i++) {
if (oThemeCheck._CUSTOMCSSCHECK.test(aRules[i].selectorText)) {
bSuccess = true;
}
}
return bSuccess;
}
function delayedCheckTheme(bFirst) {
this._iCount++;
var bEmergencyExit = this._iCount > sap.ui._maxThemeCheckCycles;
if (!checkTheme(this) && !bEmergencyExit) {
this._sThemeCheckId = jQuery.sap.delayedCall(2, this, delayedCheckTheme);
} else if (!bFirst) {
clear(this);
ThemeCheck.themeLoaded = true;
this._oCore.fireThemeChanged({theme: this._oCore.getConfiguration().getTheme()});
if (bEmergencyExit) {
jQuery.sap.log.warning("ThemeCheck: max. check cycles reached.");
}
} else {
ThemeCheck.themeLoaded = true;
}
}
return ThemeCheck;
}, /* bExport= */ true);
},
"sap/ui/core/UIArea.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.ui.core.UIArea
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObject', './Element', './RenderManager', 'jquery.sap.act', 'jquery.sap.ui'],
function(jQuery, ManagedObject, Element, RenderManager /* , jQuerySap1, jQuerySap */) {
"use strict";
/**
* A private logger instance used for 'debugRendering' logging.
*
* It can be activated by setting the URL parameter sap-ui-xx-debugRerendering to true.
* If activated, stack traces of invalidate() calls will be recorded and if new
* invalidations occur during rendering, they will be logged to the console together
* with the causing stack traces.
*
* @private
* @todo Add more log output where helpful
*/
var oRenderLog = jQuery.sap.log.getLogger("sap.ui.Rendering",
((window["sap-ui-config"] && window["sap-ui-config"]["xx-debugRendering"]) || /sap-ui-xx-debug(R|-r)endering=(true|x|X)/.test(document.location.search)) ? jQuery.sap.log.Level.DEBUG : Math.min(jQuery.sap.log.Level.INFO, jQuery.sap.log.getLevel())
),
fnDbgWrap = function(oControl) {
return oControl;
},
fnDbgReport = jQuery.noop,
fnDbgAnalyzeDelta = jQuery.noop;
if ( oRenderLog.isLoggable() ) {
// TODO this supportability feature could be moved out of the standard runtime code and only be loaded on demand
/**
* Records the stack trace that triggered the first invalidation of the given control
*
* @private
*/
fnDbgWrap = function(oControl) {
var location;
try {
throw new Error();
} catch (e) {
location = e.stack || e.stacktrace || (e.sourceURL ? e.sourceURL + ":" + e.line : null);
location = location ? location.split(/\n\s*/g).slice(2) : undefined;
}
return {
obj : oControl,
location : location
};
};
/**
* Creates a condensed view of the controls for which a rendering task is pending.
* Checking the output of this method should help to understand infinite or unexpected rendering loops.
* @private
*/
fnDbgReport = function(that, mControls) {
var oCore = sap.ui.getCore(),
mReport = {},
n, oControl;
for (n in mControls) {
// resolve oControl anew as it might have changed
oControl = oCore.byId(n);
/*eslint-disable no-nested-ternary */
mReport[n] = {
type: oControl ? oControl.getMetadata().getName() : (mControls[n].obj === that ? "UIArea" : "(no such control)"),
location: mControls[n].location,
reason : mControls[n].reason
};
/*eslint-enable no-nested-ternary */
}
oRenderLog.debug(" UIArea '" + that.getId() + "', pending updates: " + JSON.stringify(mReport, null, "\t"));
};
/**
* Creates a condensed view of the controls that have been invalidated but not handled during rendering
* Checking the output of this method should help to understand infinite or unexpected rendering loops.
* @private
*/
fnDbgAnalyzeDelta = function(mBefore, mAfter) {
var n;
for (n in mAfter) {
if ( mBefore[n] != null ) {
if ( mBefore[n].obj !== mAfter[n].obj ) {
mAfter[n].reason = "replaced during rendering";
} else {
mAfter[n].reason = "invalidated again during rendering";
}
} else {
mAfter[n].reason = "invalidated during rendering";
}
}
};
}
/**
* @class An area in a page that hosts a tree of UI elements.
*
* Provides means for event-handling, rerendering, etc.
*
* Special aggregation "dependents" is connected to the lifecycle management and databinding,
* but not rendered automatically and can be used for popups or other dependent controls. This allows
* definition of popup controls in declarative views and enables propagation of model and context
* information to them.
*
* @extends sap.ui.base.ManagedObject
* @author SAP SE
* @version 1.28.5
* @param {sap.ui.core.Core} oCore internal API of the <core>Core</code> that manages this UIArea
* @param {object} [oRootNode] reference to the Dom Node that should be 'hosting' the UI Area.
* @public
* @alias sap.ui.core.UIArea
*/
var UIArea = ManagedObject.extend("sap.ui.core.UIArea", {
constructor: function(oCore, oRootNode) {
if (arguments.length === 0) {
return;
}
// Note: UIArea has a modifiable Id. This doesn't perfectly match the default behavior of ManagedObject
// But UIArea overrides getId().
ManagedObject.apply(this);
//TODO we could get rid of oCore here, if we wanted to...
this.oCore = oCore;
this.bLocked = false;
this.bInitial = true;
this.aContentToRemove = [];
this.bNeedsRerendering = false;
if (oRootNode != null) {
this.setRootNode(oRootNode);
// Figure out whether UI Area is pre-rendered (server-side JS rendering)!
this.bNeedsRerendering = this.bNeedsRerendering && !jQuery.sap.domById(oRootNode.id + "-Init");
}
this.mInvalidatedControls = {};
if (!this.bNeedsRerendering) {
this.bRenderSelf = false;
} else {
// Core needs to be notified about an invalid UIArea
this.oCore.addInvalidatedUIArea(this);
}
},
metadata: {
// ---- object ----
publicMethods : ["setRootNode", "getRootNode", "setRootControl", "getRootControl", "lock","unlock", "isLocked"],
aggregations : {
/**
* Content that is displayed in the UIArea.
*/
content : {name : "content", type : "sap.ui.core.Control", multiple : true, singularName : "content"},
/**
* Dependent objects whose lifecycle is bound to the UIarea but which are not automatically rendered by the UIArea.
*/
dependents : {name : "dependents", type : "sap.ui.core.Control", multiple : true}
}
}
});
/**
* Returns whether rerendering is currently suppressed on this UIArea
* @return boolean
* @protected
*/
UIArea.prototype.isInvalidateSuppressed = function() {
return this.iSuppressInvalidate > 0;
};
/**
* Returns this <code>UIArea</code>'s id (as determined from provided RootNode).
* @return {string|null} id of this UIArea
* @public
*/
UIArea.prototype.getId = function() {
return this.oRootNode ? this.oRootNode.id : null;
};
/**
* Returns this UI area. Needed to stop recursive calls from an element to its parent.
*
* @return {sap.ui.core.UIArea} this
* @protected
*/
UIArea.prototype.getUIArea = function() {
return this;
};
/**
* Allows setting the Root Node hosting this instance of <code>UIArea</code>.<br/> The Dom Ref must have an Id that
* will be used as Id for this instance of <code>UIArea</code>.
*
* @param {object}
* oRootNode the hosting Dom Ref for this instance of <code>UIArea</code>.
* @public
*/
UIArea.prototype.setRootNode = function(oRootNode) {
if (this.oRootNode === oRootNode) {
return;
}
// oRootNode must either be empty or must be a DOMElement and must not be root node of some other UIArea
jQuery.sap.assert(!oRootNode || (oRootNode.nodeType === 1 && !jQuery(oRootNode).attr("data-sap-ui-area")), "UIArea root node must be a DOMElement");
//TODO IS there something missing
if (this.oRootNode) {
this._ondetach();
}
this.oRootNode = oRootNode;
if ( this.getContent().length > 0 ) {
this.invalidate();
}
if (this.oRootNode) {
// prepare eventing
this._onattach();
}
};
/**
* Returns the Root Node hosting this instance of <code>UIArea</code>.
*
* @return {Element} the Root Node hosting this instance of <code>UIArea</code>.
* @public
*/
UIArea.prototype.getRootNode = function() {
return this.oRootNode;
};
/**
* Sets the root control to be displayed in this UIArea.
*
* First, all old content controls (if any) will be detached from this UIArea (e.g. their parent
* relationship to this UIArea will be cut off). Then the parent relationship for the new
* content control (if not empty) will be set to this UIArea and finally, the UIArea will
* be marked for re-rendering.
*
* The real re-rendering happens whenever the re-rendering is called. Either implicitly
* at the end of any control event or by calling sap.ui.getCore().applyChanges().
*
* @param {sap.ui.base.Interface | sap.ui.core.Control}
* oRootControl the Control that should be the Root for this <code>UIArea</code>.
* @public
* @deprecated use functions <code>removeAllContent</code> and <code>addContent</code> instead
*/
UIArea.prototype.setRootControl = function(oRootControl) {
this.removeAllContent();
this.addContent(oRootControl);
};
/**
* Returns the content control of this <code>UIArea</code> at the specified index.
* If no index is given the first content control is returned.
*
* @param {int} idx index of the control in the content of this <code>UIArea</code>
* @return {sap.ui.core.Control} the content control of this <code>UIArea</code> at the specified index.
* @public
* @deprecated use function <code>getContent</code> instead
*/
UIArea.prototype.getRootControl = function(idx) {
var aContent = this.getContent();
if (aContent.length > 0) {
if (idx >= 0 && idx < aContent.length) {
return aContent[idx];
}
return aContent[0];
}
return null;
};
UIArea.prototype._addRemovedContent = function(oDomRef) {
if (this.oRootNode && oDomRef) {
this.aContentToRemove.push(oDomRef);
}
};
/*
* See generated JSDoc
*/
UIArea.prototype.addContent = function(oContent, _bSuppressInvalidate) {
this.addAggregation("content", oContent, _bSuppressInvalidate);
// TODO this remains here just to make the UX3 Shell work which doesn't invalidate properly
if ( _bSuppressInvalidate !== true ) {
this.invalidate();
}
return this;
};
/*
* See generated JSDoc
*/
UIArea.prototype.removeContent = function(vContent, /* internal only */ _bSuppressInvalidate) {
var oContent = this.removeAggregation("content", vContent, _bSuppressInvalidate);
if ( !_bSuppressInvalidate ) {
var oDomRef;
if (oContent && oContent.getDomRef) {
oDomRef = oContent.getDomRef();
}
this._addRemovedContent(oDomRef);
//this.invalidate();
}
return oContent;
};
/*
* See generated JSDoc
*/
UIArea.prototype.removeAllContent = function() {
var aContent = this.removeAllAggregation("content");
for (var idx = 0; idx < aContent.length; idx++) {
var oDomRef;
var oContent = aContent[idx];
if (oContent && oContent.getDomRef) {
oDomRef = oContent.getDomRef();
}
this._addRemovedContent(oDomRef);
}
//this.invalidate();
return aContent;
};
/*
* See generated JSDoc
*/
UIArea.prototype.destroyContent = function() {
var aContent = this.getContent();
for (var idx = 0; idx < aContent.length; idx++) {
var oDomRef;
var oContent = aContent[idx];
if (oContent && oContent.getDomRef) {
oDomRef = oContent.getDomRef();
}
this._addRemovedContent(oDomRef);
}
this.destroyAggregation("content");
//this.invalidate();
return this;
};
/**
* Locks this instance of UIArea.
*
* Rerendering and eventing will not be active as long as no
* {@link #unlock} is called.
*
* @public
*/
UIArea.prototype.lock = function() {
this.bLocked = true;
};
/**
* Un-Locks this instance of UIArea.
*
* Rerendering and eventing will now be enabled again.
*
* @public
*/
UIArea.prototype.unlock = function() {
if ( this.bLocked && this.bNeedsRerendering ) {
// While being locked, we might have ignored a call to rerender()
// Therefore notify the Core (again)
this.oCore.addInvalidatedUIArea(this);
}
this.bLocked = false;
};
/**
* Returns the locked state of the <code>sap.ui.core.UIArea</code>
* @return {boolean} locked state
* @public
*/
UIArea.prototype.isLocked = function () {
return this.bLocked;
};
/**
* Provide getBindingContext, as UIArea can be parent of an element.
* @return {null} Always returns null.
*
* @protected
*/
UIArea.prototype.getBindingContext = function(){
return null;
};
/**
* Returns the Core as new eventing parent to enable control event bubbling to the core to ensure compatibility with the core validation events.
*
* @return {sap.ui.base.EventProvider} the parent event provider
* @protected
*/
UIArea.prototype.getEventingParent = function() {
return this.oCore;
};
// ###########################################################################
// Convenience for methods
// e.g. Process Events for inner Controls
// or figure out whether control is part of this area.
// ###########################################################################
/**
* Checks whether the control is still valid (is in the DOM)
*
* @return {boolean} True if the control is still in the active DOM
* @protected
*/
UIArea.prototype.isActive = function() {
return jQuery.sap.domById(this.getId()) != null;
};
/**
* Will be used as end-point for invalidate-bubbling from controls up their hierarchy.<br/> Triggers re-rendering of
* the UIAreas content.
* @protected
*/
UIArea.prototype.invalidate = function() {
this.addInvalidatedControl(this);
};
/**
* Notifies the UIArea about an just invalidated control.
*
* The UIArea internally decides whether to re-render just the modified
* controls or the complete content. It also informs the Core when it
* becomes invalid the first time.
*
* @param {object} oControl
* @private
*/
UIArea.prototype.addInvalidatedControl = function(oControl){
// if UIArea is already marked for a full rendering, there is no need to record invalidated controls
if ( this.bRenderSelf ) {
return;
}
// inform the Core, if we are getting invalid now
if ( !this.bNeedsRerendering ) {
this.oCore.addInvalidatedUIArea(this);
}
var sId = oControl.getId();
//check whether the control is already invalidated
if (/*jQuery.inArray(oControl, this.getContent()) || */oControl === this ) {
this.bRenderSelf = true; //everything in this UIArea
this.bNeedsRerendering = true;
this.mInvalidatedControls = {};
this.mInvalidatedControls[sId] = fnDbgWrap(this);
return;
}
if (this.mInvalidatedControls[sId]) {
return;
}
if (!this.bRenderSelf) {
//add it to the list of controls
this.mInvalidatedControls[sId] = fnDbgWrap(oControl);
this.bNeedsRerendering = true;
}
};
/**
* Renders any pending UI updates.
*
* Either renders the whole UIArea or a set of descendent controls that have been invalidated.
*
* @param {boolean} force true, if the rerendering of the UI area should be forced
* @return {boolean} whether a redraw was necessary or not
* @private
*/
UIArea.prototype.rerender = function(force){
var that = this;
function clearRenderingInfo() {
that.bRenderSelf = false;
that.aContentToRemove = [];
that.mInvalidatedControls = {};
that.bNeedsRerendering = false;
}
if (force) {
this.bNeedsRerendering = true;
}
if ( this.bLocked || !this.bNeedsRerendering ) {
return false;
}
// Keep a reference to the collected rendering info and attach a new, empty info to this instance.
// Any concurrent modification will be collected as new info and trigger a new automated rendering
var bRenderSelf = this.bRenderSelf,
aContentToRemove = this.aContentToRemove,
mInvalidatedControls = this.mInvalidatedControls,
bUpdated = false;
clearRenderingInfo();
// pause performance measurement for all UI Areas
jQuery.sap.measure.pause("renderPendingUIUpdates");
// start performance measurement
jQuery.sap.measure.start(this.getId() + "---rerender","Rerendering of " + this.getMetadata().getName());
fnDbgReport(this, mInvalidatedControls);
if (bRenderSelf) { // full UIArea rendering
if (this.oRootNode) {
oRenderLog.debug("Full Rendering of UIArea '" + this.getId() + "'");
// save old content
RenderManager.preserveContent(this.oRootNode, /* bPreserveRoot */ false, /* bPreserveNodesWithId */ this.bInitial);
this.bInitial = false;
var cleanUpDom = function(aCtnt, bCtrls){
var len = aCtnt.length;
var oDomRef;
for (var i = 0; i < len; i++) {
oDomRef = bCtrls ? aCtnt[i].getDomRef() : aCtnt[i];
if ( oDomRef && !RenderManager.isPreservedContent(oDomRef) && that.oRootNode === oDomRef.parentNode) {
jQuery(oDomRef).remove();
}
}
return len;
};
//First remove the old Dom nodes and then render the controls again
cleanUpDom(aContentToRemove);
var aContent = this.getContent();
var len = cleanUpDom(aContent, true);
for (var i = 0; i < len; i++) {
this.oCore.oRenderManager.render(aContent[i], this.oRootNode, true);
}
bUpdated = true;
} else {
// cannot re-render now; wait!
oRenderLog.debug("Full Rendering of UIArea '" + this.getId() + "' postponed, no root node");
}
} else { // only partial update (invalidated controls)
var isAncestorInvalidated = function(oAncestor) {
while ( oAncestor && oAncestor !== that ) {
if ( mInvalidatedControls.hasOwnProperty(oAncestor.getId()) ) {
return true;
}
// Controls that implement marker interface sap.ui.core.PopupInterface are by contract not rendered by their parent.
// Therefore the search for invalid ancestors must be stopped when such a control is reached.
if ( oAncestor && oAncestor.getMetadata && oAncestor.getMetadata().isInstanceOf("sap.ui.core.PopupInterface") ) {
break;
}
oAncestor = oAncestor.getParent();
}
return false;
};
for (var n in mInvalidatedControls) { // TODO for in skips some names in IE8!
var oControl = this.oCore.byId(n);
// CSN 0000834961 2011: control may have been destroyed since invalidation happened
if ( oControl && !isAncestorInvalidated(oControl.getParent()) ) {
oControl.rerender();
bUpdated = true;
}
}
}
// enrich the bookkeeping
fnDbgAnalyzeDelta(mInvalidatedControls, this.mInvalidatedControls);
// uncomment the following line for old behavior:
// clearRenderingInfo();
// end performance measurement
jQuery.sap.measure.end(this.getId() + "---rerender");
// resume performance measurement for all UI Areas
jQuery.sap.measure.resume("renderPendingUIUpdates");
return bUpdated;
};
/**
* Receives a notification from the RenderManager immediately after a control has been rendered.
*
* Only at that moment, registered invalidations are obsolete. If they happen (again) after
* that point in time, the previous rendering cannot reflect the changes that led to the
* invalidation and therefore a new rendering is required.
*
* Therefore, pending invalidatoins can only be cleared at this point in time.
* @private
*/
UIArea.prototype._onControlRendered = function(oControl) {
var sId = oControl.getId();
if ( this.mInvalidatedControls[sId] ) {
delete this.mInvalidatedControls[sId];
}
};
/**
* Rerenders the given control
* @see sap.ui.core.Control.rerender()
* @param oControl
* @private
*/
UIArea.rerenderControl = function(oControl){
var oDomRef = null;
if (oControl) {
oDomRef = oControl.getDomRef();
if (!oDomRef) {
// If no DOM node was found, look for the invisible placeholder node
oDomRef = jQuery.sap.domById(sap.ui.core.RenderPrefixes.Invisible + oControl.getId());
}
}
var oParentDomRef = oDomRef && oDomRef.parentNode; // remember parent here as preserveContent() might move the node!
if (oParentDomRef) {
var uiArea = oControl.getUIArea();
var rm = uiArea ? uiArea.oCore.oRenderManager : sap.ui.getCore().createRenderManager();
oRenderLog.debug("Rerender Control '" + oControl.getId() + "'" + (uiArea ? "" : " (using a temp. RenderManager)"));
RenderManager.preserveContent(oDomRef, /* bPreserveRoot */ true, /* bPreserveNodesWithId */ false);
rm.render(oControl, oParentDomRef);
} else {
var uiArea = oControl.getUIArea();
uiArea && uiArea._onControlRendered(oControl);
oRenderLog.warning("Couldn't rerender '" + oControl.getId() + "', as its DOM location couldn't be determined");
}
};
/**
* Handles all incoming DOM events centrally and dispatches the event to the
* registered event handlers.
* @param {jQuery.Event} oEvent the jQuery event object
* @private
*/
UIArea.prototype._handleEvent = function(/**event*/oEvent) {
// execute the registered event handlers
var oElement = null;
// TODO: this should be the 'lowest' SAPUI5 Control of this very
// UIArea instance's scope -> nesting scenario
oElement = jQuery(oEvent.target).control(0);
jQuery.sap.act.refresh();
if (oElement === null) {
return;
}
// the mouse event which is fired by mobile browser with a certain delay after touch event should be suppressed
// in event delegation.
if (oEvent.isMarked("delayedMouseEvent")) {
return;
}
//if event is already handled by inner UIArea (as we use the bubbling phase now), returns.
//if capturing phase would be used, here means event is already handled by outer UIArea.
if (oEvent.isMarked("handledByUIArea")) {
oEvent.setMark("firstUIArea", false);
return;
}
oEvent.setMarked("firstUIArea");
// store the element on the event (aligned with jQuery syntax)
oEvent.srcControl = oElement;
// in case of CRTL+SHIFT+ALT the contextmenu event should not be dispatched
// to allow to display the browsers context menu
if (oEvent.type === "contextmenu" && oEvent.shiftKey && oEvent.altKey && !!(oEvent.metaKey || oEvent.ctrlKey)) {
jQuery.sap.log.info("Suppressed forwarding the contextmenu event as control event because CTRL+SHIFT+ALT is pressed!");
return;
}
// forward the control event:
// if the control propagation has been stopped or the default should be
// prevented then do not forward the control event.
this.oCore._handleControlEvent(oEvent, this.getId());
// if the UIArea or the Core is locked then we do not dispatch
// any event to the control => but they will still be dispatched
// as control event afterwards!
if (this.bLocked || this.oCore.isLocked()) {
return;
}
// retrieve the pseudo event types
var aEventTypes = [];
if (oEvent.getPseudoTypes) {
aEventTypes = oEvent.getPseudoTypes();
}
aEventTypes.push(oEvent.type);
// dispatch the event to the controls (callback methods: onXXX)
while (oElement && oElement instanceof Element && oElement.isActive() && !oEvent.isPropagationStopped()) {
// for each event type call the callback method
// if the execution should be stopped immediately
// then no further callback method will be executed
for (var i = 0, is = aEventTypes.length; i < is; i++) {
var sType = aEventTypes[i];
oEvent.type = sType;
// ensure currenTarget is the DomRef of the handling Control
oEvent.currentTarget = oElement.getDomRef();
oElement._handleEvent(oEvent);
if (oEvent.isImmediatePropagationStopped()) {
break;
}
}
// if the propagation is stopped do not bubble up further
if (oEvent.isPropagationStopped()) {
break;
}
// Secret property on the element to allow to cancel bubbling of all events.
// This is a very special case, so there is no API method for this in the control.
if (oElement.bStopEventBubbling) {
break;
}
// This is the (not that common) situation that the element was deleted in its own event handler.
// i.e. the Element became 'inactive' (see Element#isActive())
var oDomRef = oElement.getDomRef();
if (!oDomRef) {
break;
}
// bubble up to the parent
oDomRef = oDomRef.parentNode;
oElement = null;
// Only process the touchend event which is emulated from mouseout event when the current domRef
// doesn't equal or contain the relatedTarget
if (oEvent.isMarked("fromMouseout") && jQuery.sap.containsOrEquals(oDomRef, oEvent.relatedTarget)) {
break;
}
// ensure we do not bubble the control tree higher than our rootNode
while (oDomRef && oDomRef !== this.getRootNode()) {
if (oDomRef.id) {
oElement = jQuery(oDomRef).control(0);
if (oElement) {
break;
}
}
oDomRef = oDomRef.parentNode;
}
}
// reset previously changed currentTarget
oEvent.currentTarget = this.getRootNode();
// mark on the event that it's already handled by this UIArea
(oEvent.originalEvent || oEvent)._sapui_handledByUIArea = true;
// TODO: rethink about logging levels!
// logging: propagation stopped
if (oEvent.isPropagationStopped()) {
jQuery.sap.log.debug("'" + oEvent.type + "' propagation has been stopped");
}
// logging: prevent the logging of some events and for others do some
// info logging into the console
var sName = oEvent.type;
if (sName != "mousemove" && sName != "mouseover" && sName != "scroll" && sName != "mouseout") {
var oElem = jQuery(oEvent.target).control(0);
if (oElem) {
jQuery.sap.log.debug("Event fired: '" + oEvent.type + "' on " + oElem, "", "sap.ui.core.UIArea");
} else {
jQuery.sap.log.debug("Event fired: '" + oEvent.type + "'", "", "sap.ui.core.UIArea");
}
}
};
/*
* The onattach function is called when the Element is attached to the DOM
* @private
*/
UIArea.prototype._onattach = function() {
// TODO optimizations for 'matching event list' could be done here.
// // create the events string (space separated list of event names):
// // the first time a control is attached - it will determine the required
// // events and store this information in the controls metadata which is
// // shared across the control instances.
// if (!this.getMetadata().sEvents) {
//
// // shorten the access to the array of events and pseudo events
// var aEv = jQuery.sap.ControlEvents;
// var oPsEv = jQuery.sap.PseudoEvents;
//
// // create the data structures for the event handler registration
// this.sEvents = "";
// var aEvents = [];
//
// // check for pseudo events and register them for their relevant types
// for (var evt in oPsEv) {
// for (j = 0, js = oPsEv[evt].aTypes.length; j < js; j++) {
// var type = oPsEv[evt].aTypes[j];
// if (jQuery.inArray(type, aEvents) == -1) {
// aEvents.push(type);
// }
// }
// }
//
// // check for events and register them
// for (var i = 0, is = aEv.length; i < is; i++) {
// var type = aEv[i];
// if (jQuery.inArray(type, aEvents) == -1) {
// aEvents.push(type);
// }
// }
//
// // keep the list of events for the jQuery bind/unbind method
// this.sEvents = aEvents.join(" ");
//
// // cache the event handlers registry map
// this.getMetadata().sEvents = this.sEvents;
//
// } else {
// // use the cached map of event handlers
// this.sEvents = this.getMetadata().sEvents;
// }
// check for existing root node
var oDomRef = this.getRootNode();
if (oDomRef == null) {
return;
}
// mark the DOM as UIArea and bind the required events
jQuery(oDomRef).attr("data-sap-ui-area", oDomRef.id).bind(jQuery.sap.ControlEvents.join(" "), jQuery.proxy(this._handleEvent, this));
};
/**
* The ondetach function is called when the Element is detached out of the DOM
* @private
*/
UIArea.prototype._ondetach = function() {
// check for existing root node
var oDomRef = this.getRootNode();
if (oDomRef == null) {
return;
}
// remove UIArea marker and unregister all event handlers of the control
jQuery(oDomRef).removeAttr("data-sap-ui-area").unbind();
// TODO: when optimizing the events => take care to unbind only the
// required. additionally consider not to remove other event handlers.
// var ojQRef = jQuery(oDomRef);
// if (this.sEvents) {
// ojQRef.unbind(this.sEvents, this._handleEvent);
// }
//
// var oFH = this.oCore.oFocusHandler;
// ojQRef.unbind("focus",oFH.onfocusin);
// ojQRef.unbind("blur", oFH.onfocusout);
};
/**
* An UIArea can't be cloned and throws an error when trying to do so.
*/
UIArea.prototype.clone = function() {
throw new Error("UIArea can't be cloned");
};
// share the render log with Core
UIArea._oRenderLog = oRenderLog;
return UIArea;
}, /* bExport= */ true);
},
"sap/ui/core/message/ControlMessageProcessor.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the implementation for the ControlControlMessageProcessor implementations
sap.ui.define(['jquery.sap.global', 'sap/ui/core/message/MessageProcessor'],
function(jQuery, MessageProcessor) {
"use strict";
/**
*
* @namespace
* @name sap.ui.core.message
* @public
*/
/**
* Constructor for a new ControlMessageProcessor
*
* @class
* The ControlMessageProcessor implementation.
* This MessageProcessor is able to handle Messages with the following target syntax:
* 'ControlID/PropertyName'
*
* @extends sap.ui.base.EventProvider
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @public
* @alias sap.ui.core.message.ControlMessageProcessor
*/
var ControlMessageProcessor = MessageProcessor.extend("sap.ui.core.message.ControlMessageProcessor", /** @lends sap.ui.core.message.ControlMessageProcessor.prototype */ {
constructor : function () {
MessageProcessor.apply(this, arguments);
},
metadata : {
}
});
/**
* Set Messages to check
* @param {map}
* vMessages map of messages: {'target': [array of messages],...}
* @protected
*/
ControlMessageProcessor.prototype.setMessages = function(vMessages) {
this.mOldMessages = jQuery.isEmptyObject(this.mMessages) ? vMessages : this.mMessages;
this.mMessages = vMessages || {};
this.checkMessages();
delete this.mOldMessages;
};
/**
* Check Messages and update controls with messages
* @protected
*/
ControlMessageProcessor.prototype.checkMessages = function() {
var aMessages,
that = this;
jQuery.each(this.mOldMessages, function(sTarget, aOldMessages) {
var oBinding;
var aParts = sTarget.split('/');
var oControl = sap.ui.getCore().byId(aParts[0]);
//if control does not exist: nothing to do
if (!oControl) {
return;
}
oBinding = oControl.getBinding(aParts[1]);
aMessages = that.mMessages[sTarget] ? that.mMessages[sTarget] : [];
if (oBinding) {
oBinding._fireMessageChange({messageSource: 'control', messages:aMessages});
} else {
oControl.updateMessages(aParts[1], aMessages);
}
});
};
return ControlMessageProcessor;
}, /* bExport= */ true);
},
"sap/ui/core/message/Message.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the implementation for a Message
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Object'],
function(jQuery, Object) {
"use strict";
/**
*
* @namespace
* @name sap.ui.core.message
* @public
*/
/**
* Constructor for a new Message.
* @class
* @extends sap.ui.base.Object
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
*
* @param {object} [mParameters] (optional) a map which contains the following parameter properties:
* {string} [mParameters.id] The message id: will be defaulted if no id is set
* {string} [mParameters.message] The message text
* {string} [mParameters.description] The message description
* {sap.ui.core.MessageType} [mParameters.type] The message type
* {string} [mParameters.code] The message code
* {sap.ui.core.message.Messageprocessor} [mParameters.processor]
* {string} [mParameters.target] The message target: The syntax MessageProcessor dependent. Read the documentation of the respective MessageProcessor.
* {boolean} [mParameters.persistent] Sets message persistent: If persistent is set true the message
* lifecycle controlled by Application
*
* @public
* @alias sap.ui.core.message.Message
*/
var Message = Object.extend("sap.ui.core.message.Message", /** @lends sap.ui.core.message.Message.prototype */ {
constructor : function (mParameters) {
Object.apply(this, arguments);
this.id = mParameters.id ? mParameters.id : jQuery.sap.uid();
this.message = mParameters.message;
this.description = mParameters.description;
this.type = mParameters.type;
this.code = mParameters.code;
this.target = mParameters.target;
this.processor = mParameters.processor;
this.persistent = mParameters.persistent || false;
this.technical = mParameters.technical || false;
}
});
/**
* Returns the Message Id
*
* @returns {string} id
*/
Message.prototype.getId = function() {
return this.id;
};
/**
* Set message text
*
* @param {string} sMessage The Message as text
*/
Message.prototype.setMessage = function(sMessage) {
this.message = sMessage;
};
/**
* Returns message text
*
* @returns {string} message
*/
Message.prototype.getMessage = function() {
return this.message;
};
/**
* Set message description
*
* @param {string} sDescription The Message description
*/
Message.prototype.setDescription = function(sDescription) {
this.description = sDescription;
};
/**
* Returns the message description
*
* @returns {string} description
*/
Message.prototype.getDescription = function() {
return this.description;
};
/**
* Set message type
*
* @param {sap.ui.core.MessageType} sType The Message type
*/
Message.prototype.setType = function(sType) {
if (sType in sap.ui.core.MessageType) {
this.type = sType;
} else {
jQuery.sap.log.error("MessageType must be of type sap.ui.core.MessageType");
}
};
/**
* Returns the message type
*
* @returns {sap.ui.core.MessageType} type
*/
Message.prototype.getType = function() {
return this.type;
};
/**
* Set message target: The syntax MessageProcessor dependent. See the documentation of the
* respective MessageProcessor.
*
* @param {string} sTarget The Message target
*/
Message.prototype.setTarget = function(sTarget) {
this.target = sTarget;
};
/**
* Returns the message target
*
* @returns {string} target
*/
Message.prototype.getTarget = function() {
return this.target;
};
/**
* Set message processor
*
* @param {sap.ui.core.message.MessageProcessor} oMessageProcessor The Message processor
*/
Message.prototype.setMessageProcessor = function(oMessageProcessor) {
if (oMessageProcessor instanceof sap.ui.core.message.MessageProcessor) {
this.processor = oMessageProcessor;
} else {
jQuery.sap.log.error("MessageProcessor must be an instance of sap.ui.core.message.MessageProcessor");
}
};
/**
* Returns the message processor
*
* @returns {sap.ui.core.message.MessageProcessor} processor
*/
Message.prototype.getMessageProcessor = function() {
return this.processor;
};
/**
* Set message code
*
* @param {string} sCode The Message code
*/
Message.prototype.setCode = function(sCode) {
this.code = sCode;
};
/**
* Returns the message code
*
* @returns {string} code
*/
Message.prototype.getCode = function() {
return this.code;
};
/**
* Set message persistent
*
* @param {boolean} bPersistent Set Message persistent: If persisten is set true the message
* lifecycle controlled by Application
*/
Message.prototype.setPersistent = function(bPersistent) {
this.persistent = bPersistent;
};
/**
* Returns the if Message is persistent
*
* @returns {boolean} bPersistent
*/
Message.prototype.getPersistent = function() {
return this.persistent;
};
/**
* Set message as technical message
*
* @param {boolean} bTechnical Set Message as technical message
* lifecycle controlled by Application
*/
Message.prototype.setTechnical = function(bTechnical) {
this.technical = bTechnical;
};
/**
* Returns the if Message set as technical message
*
* @returns {boolean} bTechnical
*/
Message.prototype.getTechnical = function() {
return this.technical;
};
return Message;
}, /* bExport= */ true);
},
"sap/ui/core/message/MessageManager.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the implementation for a MessageManager
sap.ui.define(['jquery.sap.global', 'sap/ui/base/EventProvider', 'sap/ui/model/message/MessageModel', './Message', './ControlMessageProcessor'],
function(jQuery, EventProvider, MessageModel, Message, ControlMessageProcessor) {
"use strict";
/**
*
* @namespace
* @name sap.ui.core.message
* @public
*/
/**
* Constructor for a new MessageManager.
*
* @class
*
* @extends sap.ui.base.EventProvider
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @public
* @alias sap.ui.core.message.MessageManager
*/
var MessageManager = EventProvider.extend("sap.ui.core.message.MessageManager", /** @lends sap.ui.core.message.MessageManager.prototype */ {
constructor : function () {
EventProvider.apply(this, arguments);
this.mProcessors = {};
this.mObjects = {};
this.mMessages = {};
var bHandleValidation = sap.ui.getCore().getConfiguration().getHandleValidation();
if (bHandleValidation) {
sap.ui.getCore().attachValidationSuccess(bHandleValidation, this._handleSuccess, this);
sap.ui.getCore().attachValidationError(bHandleValidation, this._handleError, this);
sap.ui.getCore().attachParseError(bHandleValidation, this._handleError, this);
sap.ui.getCore().attachFormatError(bHandleValidation, this._handleError, this);
}
},
metadata : {
publicMethods : [
// methods
"addMessages", "removeMessages", "removeAllMessages", "registerMessageProcessor", "unregisterMessageProcessor", "registerObject", "unregisterObject", "getMessageModel", "destroy"
]
}
});
/**
* handle validation/parse/format error
*
* @param {object} oEvent The Event object
* @private
*/
MessageManager.prototype._handleError = function(oEvent, bHandleValidation) {
if (!this.oControlMessageProcessor) {
this.oControlMessageProcessor = new ControlMessageProcessor();
}
if (bHandleValidation) {
var oElement = oEvent.getParameter("element");
var sProperty = oEvent.getParameter("property");
var sTarget = oElement.getId() + '/' + sProperty;
var sProcessorId = this.oControlMessageProcessor.getId();
var bTechnical = oEvent.sId === "formatError";
if (this.mMessages[sProcessorId] && this.mMessages[sProcessorId][sTarget]) {
this.removeMessages(this.mMessages[sProcessorId][sTarget]);
}
var oMessage = new sap.ui.core.message.Message({
type: sap.ui.core.MessageType.Error,
message: oEvent.getParameter("message"),
target: sTarget,
processor: this.oControlMessageProcessor,
technical: bTechnical
});
this.addMessages(oMessage);
}
oEvent.cancelBubble();
};
/**
* handle validation success
*
* @param {object} oEvent The Event object
* @private
*/
MessageManager.prototype._handleSuccess = function(oEvent, bHandleValidation) {
if (!this.oControlMessageProcessor) {
this.oControlMessageProcessor = new ControlMessageProcessor();
}
if (bHandleValidation) {
var oElement = oEvent.getParameter("element");
var sProperty = oEvent.getParameter("property");
var sTarget = oElement.getId() + '/' + sProperty;
var sProcessorId = this.oControlMessageProcessor.getId();
if (this.mMessages[sProcessorId] && this.mMessages[sProcessorId][sTarget]) {
this.removeMessages(this.mMessages[sProcessorId][sTarget]);
}
}
oEvent.cancelBubble();
};
/**
* Add messages to MessageManager
*
* @param {sap.ui.core.Message|array} vMessages Array of sap.ui.core.Message or single sap.ui.core.Message
* @public
*/
MessageManager.prototype.addMessages = function(vMessages) {
var oMessage = vMessages;
if (!vMessages) {
return;
}else if (jQuery.isArray(vMessages)) {
for (var i = 0; i < vMessages.length; i++) {
oMessage = vMessages[i];
this._importMessage(oMessage);
}
} else {
this._importMessage(vMessages);
}
this._updateMessageModel();
};
/**
* import message to internal map of messages
* @private
*/
MessageManager.prototype._importMessage = function(oMessage) {
var sMessageKey = oMessage.getTarget();
var sProcessorId = oMessage.getMessageProcessor().getId();
if (!this.mMessages[sProcessorId]) {
this.mMessages[sProcessorId] = {};
}
var aMessages = this.mMessages[sProcessorId][sMessageKey] ? this.mMessages[sProcessorId][sMessageKey] : [];
aMessages.push(oMessage);
this.mMessages[sProcessorId][sMessageKey] = aMessages;
};
/**
* push messages to registered MessageProcessors
* @private
*/
MessageManager.prototype._pushMessages = function() {
var that = this;
jQuery.each(this.mProcessors, function(sId, oProcessor) {
var vMessages = that.mMessages[sId] ? that.mMessages[sId] : {};
that._sortMessages(vMessages);
//push a copy
oProcessor.setMessages(vMessages);
});
};
/**
* sort messages by type 'Error', 'Warning', 'Success', 'Info'
*
* @param {map} mMessages Map of Messages: {'target':[array of Messages]}
* @private
*/
MessageManager.prototype._sortMessages = function(mMessages) {
var mSortOrder = {'Error': 0,'Warning':1,'Success':2,'Info':3};
jQuery.each(mMessages, function(sTarget, aMessages){
aMessages.sort(function(a, b){
return mSortOrder[b.severity] - mSortOrder[a.severity];
});
});
};
/**
* update MessageModel
* @private
*/
MessageManager.prototype._updateMessageModel = function() {
var aMessages = [];
if (!this.oMessageModel) {
this.oMessageModel = new MessageModel(this);
}
jQuery.each(this.mMessages, function(sProcessorId, mMessages) {
jQuery.each(mMessages, function(sKey, vMessages){
aMessages = jQuery.merge(aMessages, vMessages);
});
});
this.oMessageModel.setData(aMessages);
this._pushMessages();
};
/**
* Remove all messages
* @public
*/
MessageManager.prototype.removeAllMessages = function() {
this.aMessages = [];
this.mMessages = {};
this._updateMessageModel();
};
/**
* Remove given Messages
*
* @param {array}
* vMessages Either an Array of sap.ui.core.message.Message,
* a single sap.ui.core.message.Message
*
* @public
*/
MessageManager.prototype.removeMessages = function(vMessages) {
var that = this;
if (!vMessages || (jQuery.isArray(vMessages) && vMessages.length == 0)) {
return;
}else if (jQuery.isArray(vMessages)) {
for (var i = 0; i < vMessages.length; i++) {
that._removeMessage(vMessages[i]);
}
} else if (vMessages instanceof sap.ui.core.message.Message){
that._removeMessage(vMessages);
} else {
//map with target as key
jQuery.each(vMessages, function (sTarget, aMessages) {
that.removeMessages(aMessages);
});
}
this._updateMessageModel();
};
/**
* remove Message
*
* @param {sap.ui.core.message.Message} oMessage The Message to remove
* @private
*/
MessageManager.prototype._removeMessage = function(oMessage) {
var mMessages = this.mMessages[oMessage.getMessageProcessor().getId()];
if (!mMessages) {
return;
}
var aMessages = mMessages[oMessage.getTarget()];
if (aMessages) {
for (var i = 0; i < aMessages.length; i++) {
var oMsg = aMessages[i];
if (jQuery.sap.equal(oMsg, oMessage) && !oMsg.getPersistent()) {
aMessages.splice(i,1);
}
}
}
};
/**
* message change handler
* @private
*/
MessageManager.prototype.onMessageChange = function(oEvent) {
var aOldMessages = oEvent.getParameter('oldMessages');
var aNewMessages = oEvent.getParameter('newMessages');
this.removeMessages(aOldMessages);
this.addMessages(aNewMessages);
};
/**
* Register MessageProcessor
*
* @param {sap.ui.core.message.MessageProcessor} oProcessor The MessageProcessor
* @public
*/
MessageManager.prototype.registerMessageProcessor = function(oProcessor) {
if (!this.mProcessors[oProcessor.getId()]) {
this.mProcessors[oProcessor.getId()] = oProcessor;
oProcessor.attachMessageChange(this.onMessageChange, this);
}
};
/**
* Deregister MessageProcessor
* @param {sap.ui.core.message.MessageProcessor} oProcessor The MessageProcessor
* @public
*/
MessageManager.prototype.unregisterMessageProcessor = function(oProcessor) {
this.removeMessages(this.mMessages[oProcessor.getId()]);
delete this.mProcessors[oProcessor.getId()];
oProcessor.detachMessageChange(this.onMessageChange);
};
/**
* Register ManagedObject: Validation and Parse errors are handled by the MessageManager for this object
*
* @param {sap.ui.base.ManageObject} oObject The sap.ui.base.ManageObject
* @param {boolean} bHandleValidation Handle validation for this object. If set to true validation/parse events creates Messages and cancel event.
* If set to false only the event will be canceled, but no messages will be created
* @public
*/
MessageManager.prototype.registerObject = function(oObject, bHandleValidation) {
if (!oObject instanceof sap.ui.base.ManagedObject) {
jQuery.sap.log.error(this + " : " + oObject.toString() + " is not an instance of sap.ui.base.ManagedObject");
return;
}
oObject.attachValidationSuccess(bHandleValidation, this._handleSuccess, this);
oObject.attachValidationError(bHandleValidation, this._handleError, this);
oObject.attachParseError(bHandleValidation, this._handleError, this);
oObject.attachFormatError(bHandleValidation, this._handleError, this);
};
/**
* Unregister ManagedObject
*
* @param {sap.ui.base.ManageObject} oObject The sap.ui.base.ManageObject
* @public
*/
MessageManager.prototype.unregisterObject = function(oObject) {
if (!oObject instanceof sap.ui.base.ManagedObject) {
jQuery.sap.log.error(this + " : " + oObject.toString() + " is not an instance of sap.ui.base.ManagedObject");
return;
}
//oObject.getMetadata().getStereoType() + getId()
oObject.detachValidationSuccess(this._handleSuccess);
oObject.detachValidationError(this._handleError);
oObject.detachParseError(this._handleError);
oObject.detachFormatError(this._handleError);
};
/**
* destroy MessageManager
* @public
*/
MessageManager.prototype.destroy = function() {
var that = this;
//Detach handler
jQuery.each(this.mProcessors, function(sId, oProcessor) {
oProcessor.detachMessageChange(this.onMessageChange);
});
jQuery.each(this.mObjects, function(sId, oObject) {
oObject.detachValidationSuccess(that._handleSuccess);
oObject.detachValidationError(that._handleError);
oObject.detachParseError(that._handleError);
oObject.detachFormatError(that._handleError);
//TODO: delete Messages for Objects
});
if (sap.ui.getCore().getConfiguration().getHandleValidation()) {
sap.ui.getCore().detachValidationSuccess(this._handleSuccess);
sap.ui.getCore().detachValidationError(this._handleError);
sap.ui.getCore().detachParseError(this._handleError);
sap.ui.getCore().detachFormatError(this._handleError);
}
this.mProcessors = undefined;
this.mMessages = undefined;
this.mObjects = undefined;
this.oMessageModel.destroy();
};
/**
* Get the MessageModel
* @return {sap.ui.core.message.MessageModel} oMessageModel The Message Model
* @public
*/
MessageManager.prototype.getMessageModel = function() {
if (!this.oMessageModel) {
this.oMessageModel = new MessageModel(this);
}
return this.oMessageModel;
};
return MessageManager;
}, /* bExport= */ true);
},
"sap/ui/core/message/MessageProcessor.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base implementation for all MessageProcessor implementations
sap.ui.define(['jquery.sap.global', 'sap/ui/base/EventProvider'],
function(jQuery, EventProvider) {
"use strict";
/**
*
* @namespace
* @name sap.ui.core.message
* @public
*/
/**
* Constructor for a new MessageProcessor
*
* @class
* This is an abstract base class for MessageProcessor objects.
* @abstract
*
* @extends sap.ui.base.EventProvider
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @public
* @alias sap.ui.core.message.MessageProcessor
*/
var MessageProcessor = EventProvider.extend("sap.ui.core.message.MessageProcessor", /** @lends sap.ui.core.message.MessageProcessor.prototype */ {
constructor : function () {
EventProvider.apply(this, arguments);
this.mMessages = {};
this.id = jQuery.sap.uid();
sap.ui.getCore().getMessageManager().registerMessageProcessor(this);
},
metadata : {
"abstract" : true,
publicMethods : [
// methods
"getId", "setMessages", "attachMessageChange", "detachMessageChange"
]
}
});
/**
* Map of event names, that are provided by the MessageProcessor.
*/
MessageProcessor.M_EVENTS = {
/**
* MessageChange should be fired when the MessageProcessor provides message changes
*
*/
messageChange : "messageChange"
};
/**
* The 'messageChange' event is fired, when the messages are changed
*
* @namesap.ui.core.messages.MessageProcessor#requestFailed
* @event
* @param {sap.ui.base.Event} oControlEvent
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the 'messageChange' event of this <code>sap.ui.core.message.MessageProcessor</code>.<br/>
*
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event.
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this MessageProcessor is used.
*
* @return {sap.ui.core.message.MessageProcessor} <code>this</code> to allow method chaining
* @public
*/
MessageProcessor.prototype.attachMessageChange = function(oData, fnFunction, oListener) {
this.attachEvent("messageChange", oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'sap.ui.core.message.MessageProcessor' event of this <code>sap.ui.core.message.MessageProcessor</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.core.message.MessageProcessor} <code>this</code> to allow method chaining
* @public
*/
MessageProcessor.prototype.detachMessageChange = function(fnFunction, oListener) {
this.detachEvent("messageChange", fnFunction, oListener);
return this;
};
/**
* Fire event messageChange to attached listeners.
*
* @param {object} [mArguments] the arguments to pass along with the event.
*
* @return {sap.ui.core.message.MessageProcessor} <code>this</code> to allow method chaining
* @protected
*/
MessageProcessor.prototype.fireMessageChange = function(mArguments) {
this.fireEvent("messageChange", mArguments);
return this;
};
// the 'abstract methods' to be implemented by child classes
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.core.message.MessageProcessor.prototype.checkMessage
* @function
* @return {sap.ui.model.ListBinding}
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.core.message.MessageProcessor.prototype.setMessages
* @function
* @param {map}
* vMessages map of messages: {'target': [array of messages],...}
* @public
*/
/**
* Returns the ID of the MessageProcessor instance
*
* @return {string} sId The MessageProcessor ID
* @public
*/
MessageProcessor.prototype.getId = function() {
return this.id;
};
/**
* Destroys the MessageProcessor Instance
* @public
*/
MessageProcessor.prototype.destroy = function() {
sap.ui.getCore().getMessageManager().unregisterMessageProcessor(this);
EventProvider.prototype.destroy.apply(this, arguments);
};
return MessageProcessor;
}, /* bExport= */ true);
},
"sap/ui/core/tmpl/Template.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides base class sap.ui.core.tmpl.Template for all templates
sap.ui.define(['jquery.sap.global', 'sap/ui/base/ManagedObject'],
function(jQuery, ManagedObject) {
"use strict";
/**
* Creates and initializes a new template with the given <code>sId</code> and
* settings.
*
* The set of allowed entries in the <code>mSettings</code> object depends on
* the concrete subclass and is described there.
*
* @param {string}
* [sId] optional id for the new template; generated automatically if
* no non-empty id is given Note: this can be omitted, no matter
* whether <code>mSettings</code> will be given or not!
* @param {object}
* [mSettings] optional map/JSON-object with initial settings for the
* new component instance
* @public
*
* @class Base Class for Template.
* @extends sap.ui.base.ManagedObject
* @abstract
* @author SAP SE
* @version 1.28.5
* @alias sap.ui.core.tmpl.Template
* @experimental Since 1.15.0. The Template concept is still under construction, so some implementation details can be changed in future.
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var Template = ManagedObject.extend("sap.ui.core.tmpl.Template", /** @lends sap.ui.core.tmpl.Template.prototype */
{
constructor : function(sId, mSettings) {
ManagedObject.apply(this, arguments);
},
metadata : {
stereotype : "template",
"abstract" : true,
library : "sap.ui.core",
properties : {
/**
* The Template definition as a String.
*/
"content" : {type : "string", group : "Data", defaultValue : null}
},
publicMethods : [
// methods
"declareControl", /* protected */
"createControl", /* protected */
"placeAt",
"createMetadata",
"createRenderer"
]
}
});
/**
* @see sap.ui.base.Object#getInterface
* @public
*/
Template.prototype.getInterface = function() {
return this;
};
/**
* registry for supported template types
* @private
*/
Template._mSupportedTypes = {};
/**
* Registers a new Template type which is used to compile the template.
*
* @param {string} sType type of the template
* @param {string} sClass the class of the specifc Template element
*
* @static
*/
Template.registerType = function(sType, sClass) {
Template._mSupportedTypes[sType] = sClass;
};
/**
* Unregisters a Template type which is used to compile the template.
*
* @param {string} sType type of the template
*
* @static
*/
Template.unregisterType = function(sType) {
delete Template._mSupportedTypes[sType];
};
/**
* parses the given path and extracts the model and path
*
* @param {string} sPath the path
* @return {object} the model and the path
*
* @protected
* @static
*/
Template.parsePath = function(sPath) {
// TODO: wouldn't this be something central in ManagedObject?
// parse the path
var sModelName,
iSeparatorPos = sPath.indexOf(">");
// if a model name is specified in the binding path
// we extract this binding path
if (iSeparatorPos > 0) {
sModelName = sPath.substr(0, iSeparatorPos);
sPath = sPath.substr(iSeparatorPos + 1);
}
// returns the path information
return {
path: sPath,
model: sModelName
};
};
/*
* overridden to prevent instantiation of Template
* @name sap.ui.core.tmpl.Template#init
* @function
*/
Template.prototype.init = function(mSettings, oScope) {
if (this.getMetadata().getName() === "sap.ui.core.tmpl.Template") {
throw new Error("The class 'sap.ui.core.tmpl.Template' is abstract and must not be instantiated!");
}
// check for complex binding syntax
if (ManagedObject.bindingParser === sap.ui.base.BindingParser.complexParser) {
/*
* we disable the complex binding parser for Templates
* TODO: reconsider a better solution later
* @name sap.ui.core.tmpl.Template#extractBindingInfo
* @function
*/
Template.prototype.extractBindingInfo = function(oValue, bIgnoreObjects, oScope) {
ManagedObject.bindingParser = sap.ui.base.BindingParser.simpleParser;
var oReturnValue = sap.ui.core.Control.prototype.extractBindingInfo.apply(this, arguments);
ManagedObject.bindingParser = sap.ui.base.BindingParser.complexParser;
return oReturnValue;
};
}
};
/**
* Declares a new control based on this template and returns the created
* class / constructor function. The class is based on the information coming
* from the abstract functions <code>createMetadata</code> and
* <code>createRenderer</code>.
*
* @param {string} sControl the fully qualified name of the control
* @return {function} the created class / constructor function
* @public
*/
Template.prototype.declareControl = function(sControl) {
jQuery.sap.assert(!!sControl, "A fully qualified name must be specified!");
if (sControl) {
// create the new control type
var oMetadata = this.createMetadata(),
fnRenderer = this.createRenderer(),
that = this;
jQuery.sap.require("sap.ui.core.tmpl.TemplateControl");
sap.ui.core.tmpl.TemplateControl.extend(sControl, {
// the new control metadata
metadata: oMetadata,
// set the reference to the template
init: function() {
sap.ui.core.tmpl.TemplateControl.prototype.init.apply(this, arguments);
// link to the template
this.setTemplate(that);
},
// add the custom renderer function
renderer: {
renderTemplate: fnRenderer,
hasControlData: oMetadata._hasControlData
}
});
// returns the constructor function
return jQuery.sap.getObject(sControl);
}
};
/**
* Creates an anonymous TemplateControl for the Template.
*
* @param {string} sId the control ID
* @param {object} [oContext] the context for the renderer/templating
* @param {sap.ui.core.mvc.View} oView
* @return {sap.ui.core.tmpl.TemplateControl} the created control instance
* @public
*/
Template.prototype.createControl = function(sId, oContext, oView) {
// create the anonymous control instance
jQuery.sap.require("sap.ui.core.tmpl.TemplateControl");
var oControl = new sap.ui.core.tmpl.TemplateControl({
id: sId,
template: this,
context: oContext
});
// for anonymous controls the renderer functions is added to the control instance
oControl.setTemplateRenderer(this.createRenderer(oView));
// return the control
return oControl;
};
/**
* Creates an anonymous TemplateControl for the Template and places the control
* into the specified DOM element.
*
* @param {string|DomRef} oRef the id or the DOM reference where to render the template
* @param {object} [oContext] The context to use to evaluate the Template. It will be applied as value for the context property of the created control.
* @param {string|int} [vPosition] Describes the position where the control should be put into the container
* @param {boolean} bInline
* @return {sap.ui.core.tmpl.TemplateControl} the created control instance
* @public
*/
Template.prototype.placeAt = function(oRef, oContext, vPosition, bInline) {
// parameter fallback
if (typeof oContext === "string" || typeof oContext === "number") {
vPosition = oContext;
oContext = undefined;
}
// if the oRef is an ID or DomRef and the template should be rendered
// inline we lookup the context from DOM element and mark the template
// as an inline template to avoid additional elements around the template.
var sId;
if (!(oRef instanceof sap.ui.core.Control) && bInline) {
// lookup the DOM element in which to place the template
var $this = typeof oRef === "string" ? jQuery.sap.byId(oRef) : jQuery(oRef);
// the DOM element must exist
if ($this.length > 0) {
// reuse the id for the template control
sId = $this.attr("id");
oRef = $this.get(0);
// by default the context coming from sap.ui.template method will be used
// but it can be also defined on the root DOM element for inline templates
// in case of inline templates we mark them
var sContext = $this.attr("data-context");
oContext = oContext || sContext && jQuery.parseJSON(sContext);
// mark the template as inline template (to avoid extra DOM for the TemplateControl)
// for inline templates the UIArea and the TemplateControl are the same DOM element
sap.ui.core.RenderManager.markInlineTemplate($this);
}
}
// create the control (ID will be generated if not inline)
var oControl = this.createControl(sId, oContext);
// render the control into the specified domref
oControl.placeAt(oRef, vPosition);
// return the control
return oControl;
};
/**
* Returns the metadata object for the new Control class.
* This function needs to be implemented by sub classes of the Template.
*
* @return {object} the metadata object of the new control class
* @abstract
*/
Template.prototype.createMetadata = function() {
jQuery.sap.log.error("The function createMetadata is an abstract function which needs to be implemented by subclasses.");
};
/**
* Returns the renderer function for the new Control class.
* This function needs to be implemented by sub classes of the Template.
*
* @return {any} the renderer function for the new Control class.
* @abstract
*/
Template.prototype.createRenderer = function() {
jQuery.sap.log.error("The function createRenderer is an abstract function which needs to be implemented by subclasses.");
};
/**
* Creates a Template for the given id, dom reference or a configuration object.
* If no parameter is defined this function makes a lookup of DOM elements
* which are specifying a type attribute. If the value of this type attribute
* matches an registered type then the content of this DOM element will be
* used to create a new <code>Template</code> instance.
*
* If you want to lookup all kind of existing and known templates and parse them
* directly you can simply call:
* <pre>
* sap.ui.template();
* </pre>
*
* To parse a concrete DOM element you can do so by using this function in the
* following way:
* <pre>
* sap.ui.template("theTemplateId");
* </pre>
*
* Or you can pass the reference to a DOM element and use this DOM element as
* a source for the template:
* <pre>
* sap.ui.template(oDomRef);
* </pre>
*
* The last option to use this function is to pass the information via a
* configuration object. This configuration object can be used to pass a
* context for the templating framework when compiling the template:
* <pre>
* var oTemplateById = sap.ui.template({
* id: "theTemplateId",
* context: { ... }
* });
*
* var oTemplateByDomRef = sap.ui.template({
* domref: oDomRef,
* context: { ... }
* });
* </pre>
*
* It can also be used to load a template from another file:
* <pre>
* var oTemplate = sap.ui.template({
* id: "myTemplate",
* src: "myTemplate.tmpl"
* });
*
* var oTemplateWithContext = sap.ui.template({
* id: "myTemplate",
* src: "myTemplate.tmpl",
* context: { ... }
* });
* </pre>
*
* The properties of the configuration object are the following:
* <ul>
* <li><code>id</code> - the ID of the Template / the ID of the DOM element containing the source of the Template</li>
* <li><code>domref</code> - the DOM element containing the source of the Template</li>
* <li><code>type</code> - the type of the Template</li>
* <li><code>src</code> - the URL to lookup the template</li> (<i>experimental!</i>)
* <li><code>control</code> - the fully qualified name of the control to declare</li> (<i>experimental!</i>)
* </ul>
*
* @param {string|DomRef|object} [oTemplate] the id or the DOM reference to the template to lookup or an configuration object containing the src, type and eventually the id of the Template.
* @return {sap.ui.core.tmpl.Template | sap.ui.core.tmpl.Template[]} the created Template instance
* or in case of usage without parametes any array of templates is returned
*
* @public
* @static
*/
sap.ui.template = function(oTemplate) {
// when no oTemplate is defined we need to lookup the elements in the document
// and retrieve elements which have a type attribute which contains a value
// of the supported types:
if (!oTemplate) {
// lookup all kind of DOM elements for having a type which is supported
var aTemplates = [];
jQuery.each(Template._mSupportedTypes, function(sType, sClass) {
jQuery("script[type='" + sType + "'], [data-type='" + sType + "']").each(function(iIndex, oElement) {
aTemplates.push(sap.ui.template({
id: oElement.id,
domref: oElement,
type: sType,
_class: sClass /* helper to save lookup time in the supported types */
}));
});
});
return aTemplates;
} else {
// check the settings for being a string or a DOM element
if (typeof oTemplate === "string") {
return sap.ui.template({
id: oTemplate
});
} else if (oTemplate && oTemplate.tagName && oTemplate.nodeName && oTemplate.ownerDocument && oTemplate.nodeType === 1) {
// instanceof HTMLElement only works for modern browsers!
return sap.ui.template({
id: oTemplate.id,
domref: oTemplate
});
}
// apply the default values
oTemplate = jQuery.extend({
type: Template.DEFAULT_TEMPLATE
}, oTemplate);
// in case of specifiying a src attribute for the configuration object
// we load the template from a remote resource
var sId, sType, sControl, sContent, sController = false,
bLoadTemplate = typeof oTemplate.src === "string",
bInline = false;
if (bLoadTemplate) {
// load the template from the specified URL
var oResponse = jQuery.sap.sjax({
url : oTemplate.src,
dataType: "text"
});
// apply the content as template content
// set the id, type and control if defined in the object
if (oResponse.success) {
sId = oTemplate.id;
sType = oTemplate.type;
sControl = oTemplate.control;
sContent = oResponse.data;
//Check for inline template information
var rTmplInfo = /^<!--\sUI5:Template\stype=([a-z\/\-]*)\s(?:controller=([A-Za-z.]*)\s)?-->/,
aTmplInfo = sContent.match(rTmplInfo);
if (aTmplInfo) {
sType = aTmplInfo[1];
if (aTmplInfo.length == 3) {
sController = aTmplInfo[2];
}
sContent = sContent.substr(aTmplInfo[0].length);
}
} else {
throw new Error("The template could not be loaded from " + oTemplate.src + "!");
}
} else {
// retrieve the required properties
var oElement = oTemplate.domref || jQuery.sap.domById(oTemplate.id),
$element = jQuery(oElement);
bInline = false;
// lookup the missing properties
sId = oTemplate.id || oElement && oElement.id;
sType = $element.attr("type") || oTemplate.type;
sControl = $element.attr("data-control") || oTemplate.control;
// lookup if the template for the current id and check this element for
// beeing a subclass of sap.ui.core.tmpl.Template and return the existing
// instance if found
if (sId) {
var theTemplate = sap.ui.getCore().getTemplate(sId);
if (!theTemplate instanceof Template) {
throw new Error("Object for id \"" + sId + "\" is no sap.ui.core.tmpl.Template!");
} else {
if (theTemplate) {
return theTemplate;
}
}
}
// the element to parse must exist
if ($element.length === 0) {
throw new Error("DOM element for the Template with the id \"" + sId + "\" not found!");
}
// retrieve the content
sContent = $element.html();
// check the preconditions for rendering and set the render property
// if the DOM ref is part of the documents body
var sTagName = oElement.tagName.toLowerCase();
if (sTagName !== "script") {
bInline = $element.parents("body").length === 1;
}
}
// if not class is given we fallback to the type attribute on the
// template defintion.
var sClass = oTemplate._class;
if (!sClass) {
sClass = Template._mSupportedTypes[sType];
if (!sClass) {
//sType = sap.ui.core.tmpl.Template.DEFAULT_TEMPLATE;
throw new Error("The type \"" + sType + "\" is not supported.");
}
}
// require and instantiate the proper template
jQuery.sap.require(sClass);
var oClass = jQuery.sap.getObject(sClass);
// create a new instance of the template
var oInstance = new oClass({
id: sId,
content: sContent
});
// declare the control if specified
if (sControl) {
oInstance.declareControl(sControl);
}
// render inline templates immediately
if (sController) {
oInstance._sControllerName = sController;
}
// render inline templates immediately
if (bInline) {
oInstance.placeAt(sId, oTemplate.context, undefined, true);
}
// return the template instance
return oInstance;
}
};
// define and register the default template
Template.DEFAULT_TEMPLATE = "text/x-handlebars-template";
Template.registerType(Template.DEFAULT_TEMPLATE, "sap.ui.core.tmpl.HandlebarsTemplate");
return Template;
}, /* bExport= */ true);
},
"sap/ui/core/util/UnicodeNormalizer.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a polyfill for the String.prototype.normalize function for older browsers
sap.ui.define(function() {
"use strict";
/*global UNorm *///declare unusual global vars for JSLint/SAPUI5 validation
// apply polyfill if needed and when not in a mobile browser
if (String.prototype.normalize != undefined || sap.ui.Device.browser.mobile == true) {
return;
} else {
jQuery.sap.require("sap.ui.thirdparty.unorm");
jQuery.sap.require("sap.ui.thirdparty.unormdata");
/*eslint-disable no-extend-native */
String.prototype.normalize = function(str) {
/*eslint-enable no-extend-native */
switch (str) {
case 'NFC':
return UNorm.nfc(this);
case 'NFD':
return UNorm.nfd(this);
case 'NFKC':
return UNorm.nfkc(this);
case 'NFKD':
return UNorm.nfkd(this);
default:
return UNorm.nfc(this);
}
};
}
return;
}, /* bExport= */false);
},
"sap/ui/model/Binding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for model bindings
sap.ui.define(['jquery.sap.global', 'sap/ui/base/EventProvider', './ChangeReason'],
function(jQuery, EventProvider, ChangeReason) {
"use strict";
/**
* Constructor for Binding class.
*
* @class
* The Binding is the object, which holds the necessary information for a data binding,
* like the binding path and the binding context, and acts like an interface to the
* model for the control, so it is the event provider for changes in the data model
* and provides getters for accessing properties or lists.
*
* @param {sap.ui.model.Model} the model
* @param {String} sPath the path
* @param {sap.ui.model.Context} oContext the context object
* @param {object} [mParameters]
* @abstract
* @public
* @alias sap.ui.model.Binding
*/
var Binding = EventProvider.extend("sap.ui.model.Binding", /** @lends sap.ui.model.Binding.prototype */ {
constructor : function(oModel, sPath, oContext, mParameters){
EventProvider.apply(this);
this.oModel = oModel;
this.bRelative = !jQuery.sap.startsWith(sPath,'/');
this.sPath = sPath;
this.oContext = oContext;
this.vMessages = undefined;
this.mParameters = mParameters;
this.bInitial = false;
this.bSuspended = false;
},
metadata : {
"abstract" : true,
publicMethods : [
// methods
"getPath", "getContext", "getModel", "attachChange", "detachChange", "refresh", "isInitial","attachDataRequested","detachDataRequested","attachDataReceived","detachDataReceived","suspend","resume"]
}
});
// Getter
/**
* Getter for path
* @return {String} the binding path
*/
Binding.prototype.getPath = function() {
return this.sPath;
};
/**
* Getter for context
* @return {Object} the context object
*/
Binding.prototype.getContext = function() {
return this.oContext;
};
/**
* Setter for context
* @param {Object} oContext the new context object
*/
Binding.prototype.setContext = function(oContext) {
if (this.oContext != oContext) {
this.oContext = oContext;
this._fireChange();
}
};
/**
* Getter for current active messages
* @return {Object} the context object
*/
Binding.prototype.getMessages = function() {
return this.vMessages;
};
/**
* Getter for model
* @return {sap.ui.core.Model} the model
*/
Binding.prototype.getModel = function() {
return this.oModel;
};
// Eventing and related
/**
* Attach event-handler <code>fnFunction</code> to the 'change' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.attachChange = function(fnFunction, oListener) {
if (!this.hasListeners("change")) {
this.oModel.addBinding(this);
}
this.attachEvent("change", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'change' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.detachChange = function(fnFunction, oListener) {
this.detachEvent("change", fnFunction, oListener);
if (!this.hasListeners("change")) {
this.oModel.removeBinding(this);
}
};
/**
* Fire event messageChange to attached listeners.
* @param {Map}
* mArguments the arguments to pass along with the event.
* @private
*/
Binding.prototype._fireMessageChange = function(mArguments) {
this.fireEvent("messageChange", mArguments);
};
/**
* Attach event-handler <code>fnFunction</code> to the 'messageChange' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.attachMessageChange = function(fnFunction, oListener) {
this.attachEvent("messageChange", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'messageChange' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.detachMessageChange = function(fnFunction, oListener) {
this.detachEvent("messageChange", fnFunction, oListener);
};
/**
* Fire event change to attached listeners.
* @param {Map}
* mArguments the arguments to pass along with the event.
* @private
*/
Binding.prototype._fireChange = function(mArguments) {
this.fireEvent("change", mArguments);
};
/**
* Attach event-handler <code>fnFunction</code> to the 'dataRequested' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @public
*/
Binding.prototype.attachDataRequested = function(fnFunction, oListener) {
this.attachEvent("dataRequested", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'dataRequested' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @public
*/
Binding.prototype.detachDataRequested = function(fnFunction, oListener) {
this.detachEvent("dataRequested", fnFunction, oListener);
};
/**
* Fire event dataRequested to attached listeners.
* @param {Map} mArguments the arguments to pass along with the event.
* @protected
*/
Binding.prototype.fireDataRequested = function(mArguments) {
this.fireEvent("dataRequested", mArguments);
};
/**
* Attach event-handler <code>fnFunction</code> to the 'dataReceived' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @public
*/
Binding.prototype.attachDataReceived = function(fnFunction, oListener) {
this.attachEvent("dataReceived", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'dataReceived' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @public
*/
Binding.prototype.detachDataReceived = function(fnFunction, oListener) {
this.detachEvent("dataReceived", fnFunction, oListener);
};
/**
* Fire event dataReceived to attached listeners.
* @param {Map} mArguments the arguments to pass along with the event.
* @protected
*/
Binding.prototype.fireDataReceived = function(mArguments) {
this.fireEvent("dataReceived", mArguments);
};
/**
* Determines if the binding should be updated by comparing the current model against a specified model.
* @param {object} oModel The model instance to compare against
* @returns {boolean} true if this binding should be updated
* @protected
*/
Binding.prototype.updateRequired = function(oModel) {
return oModel && this.getModel() === oModel;
};
/**
* Checks whether an update of this bindings is required. If this is the case the change event of
* the binding is fired.
* The default implementation just fires the change event, if the method is called, the bForceUpdate
* parameter is ignored. Subclasses should implement this, if possible.
*
* @param {boolean} bForceUpdate
*
* @private
*/
Binding.prototype.checkUpdate = function(bForceUpdate) {
if (!this.bSuspended) {
this._fireChange({reason: ChangeReason.Change});
}
};
/**
* Checks whether an update of the messages of this binding is required.
*
* @private
*/
Binding.prototype.checkMessages = function() {
var sResolvedPath = this.oModel.resolve(this.sPath, this.oContext),
vMessages;
if (sResolvedPath) {
vMessages = this.oModel.getMessagesByPath(sResolvedPath);
if (!jQuery.sap.equal(vMessages, this.vMessages)) {
//this.vMessages = vMessages;
this.vMessages = vMessages ? [].concat(vMessages) : [];
this._fireMessageChange({messages: this.vMessages});
}
}
};
/**
* Refreshes the binding, check whether the model data has been changed and fire change event
* if this is the case. For server side models this should refetch the data from the server.
* To update a control, even if no data has been changed, e.g. to reset a control after failed
* validation, please use the parameter bForceUpdate.
*
* @param {boolean} bForceUpdate Update the bound control even if no data has been changed
*
* @public
*/
Binding.prototype.refresh = function(bForceUpdate) {
this.checkUpdate(bForceUpdate);
};
/**
* Initialize the binding. The message should be called when creating a binding.
* The default implementation calls checkUpdate(true).
*
* @protected
*/
Binding.prototype.initialize = function() {
this.checkUpdate(true);
return this;
};
/**
* _refresh for compatibility
* @private
*/
Binding.prototype._refresh = function() {
this.refresh();
};
/**
* Returns whether the binding is initial, which means it did not get an initial value yet
* @return {boolean} whether binding is initial
* @public
*/
Binding.prototype.isInitial = function() {
return this.bInitial;
};
/**
* Returns whether the binding is relative, which means it did not start with a /
* @return {boolean} whether binding is relative
* @public
*/
Binding.prototype.isRelative = function() {
return this.bRelative;
};
/**
* Attach multiple events.
*
* @param {object} oEvents
* @protected
*/
Binding.prototype.attachEvents = function(oEvents) {
if (!oEvents) {
return this;
}
var that = this;
jQuery.each(oEvents, function(sEvent, fnHandler) {
var sMethod = "attach" + sEvent.substring(0,1).toUpperCase() + sEvent.substring(1);
if (that[sMethod]) {
that[sMethod](fnHandler);
} else {
jQuery.sap.log.warning(that.toString() + " has no handler for event '" + sEvent + "'");
}
});
return this;
};
/**
* Detach multiple events-
*
* @param {object} oEvents
* @protected
*/
Binding.prototype.detachEvents = function(oEvents) {
if (!oEvents) {
return this;
}
var that = this;
jQuery.each(oEvents, function(sEvent, fnHandler) {
var sMethod = "detach" + sEvent.substring(0,1).toUpperCase() + sEvent.substring(1);
if (that[sMethod]) {
that[sMethod](fnHandler);
} else {
jQuery.sap.log.warning(that.toString() + " has no handler for event '" + sEvent + "'");
}
});
return this;
};
/**
* Attach event-handler <code>fnFunction</code> to the 'refresh' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.attachRefresh = function(fnFunction, oListener) {
this.attachEvent("refresh", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'refresh' event of this <code>sap.ui.model.Binding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
Binding.prototype.detachRefresh = function(fnFunction, oListener) {
this.detachEvent("refresh", fnFunction, oListener);
};
/**
* Fire event refresh to attached listeners.
* @param {Map} [mArguments] the arguments to pass along with the event.
* @private
*/
Binding.prototype._fireRefresh = function(mArguments) {
this.fireEvent("refresh", mArguments);
};
/**
* Suspends the binding update. No change Events will be fired
*/
Binding.prototype.suspend = function() {
this.bSuspended = true;
};
/**
* Resumes the binding update. Change events will be fired again.
*/
Binding.prototype.resume = function() {
this.bSuspended = false;
this.checkUpdate();
};
return Binding;
}, /* bExport= */ true);
},
"sap/ui/model/BindingMode.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides enumeration sap.ui.model.FilterOperator
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Binding type definitions.
*
* @namespace
* @public
* @alias sap.ui.model.BindingMode
*/
var BindingMode = /** @lends sap.ui.model.BindingMode */ {
/**
* BindingMode default means that the binding mode of the model is used
* @public
*/
Default: "Default",
/**
* BindingMode one time means value is only read from the model once
* @public
*/
OneTime: "OneTime",
/**
* BindingMode one way means from model to view
* @public
*/
OneWay: "OneWay",
/**
* BindingMode two way means from model to view and vice versa
* @public
*/
TwoWay: "TwoWay"
};
return BindingMode;
}, /* bExport= */ true);
},
"sap/ui/model/ChangeReason.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides enumeration for changes in model
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* @class
* Change Reason for ListBindings.
*
* @static
* @public
* @alias sap.ui.model.ChangeReason
*/
var ChangeReason = {
/**
* The list was sorted
* @public
*/
Sort: "sort",
/**
* The List was filtered
* @public
*/
Filter: "filter",
/**
* The list has changed
* @public
*/
Change: "change",
/**
* The list context has changed
* @public
*/
Context: "context",
/**
* The list was refreshed
* @public
*/
Refresh: "refresh"
};
return ChangeReason;
}, /* bExport= */ true);
},
"sap/ui/model/ClientContextBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for list bindings
sap.ui.define(['jquery.sap.global', './ContextBinding'],
function(jQuery, ContextBinding) {
"use strict";
/**
* Constructor for ClientContextBinding
*
* @class
* The ContextBinding is a specific binding for a setting context for the model
*
* @param {sap.ui.model.Model} oModel
* @param {String} sPath
* @param {Object} oContext
* @param {Object} [mParameters]
* @abstract
* @public
* @alias sap.ui.model.ClientContextBinding
*/
var ClientContextBinding = ContextBinding.extend("sap.ui.model.ClientContextBinding", /** @lends sap.ui.model.ClientContextBinding.prototype */ {
constructor : function(oModel, sPath, oContext, mParameters, oEvents){
ContextBinding.call(this, oModel, sPath, oContext, mParameters, oEvents);
var that = this;
oModel.createBindingContext(sPath, oContext, mParameters, function(oContext) {
that.bInitial = false;
that.oElementContext = oContext;
});
}
});
/**
* @see sap.ui.model.ContextBinding.prototype.refresh
*/
ClientContextBinding.prototype.refresh = function(bForceUpdate) {
var that = this;
//recreate Context: force update
this.oModel.createBindingContext(this.sPath, this.oContext, this.mParameters, function(oContext) {
if (that.oElementContext === oContext && !bForceUpdate) {
that.oModel.checkUpdate(true,oContext);
} else {
that.oElementContext = oContext;
that._fireChange();
}
}, true);
};
/**
* @see sap.ui.model.ContextBinding.prototype.refresh
*/
ClientContextBinding.prototype.initialize = function() {
var that = this;
//recreate Context: force update
this.oModel.createBindingContext(this.sPath, this.oContext, this.mParameters, function(oContext) {
that.oElementContext = oContext;
that._fireChange();
}, true);
};
/**
* @see sap.ui.model.ContextBinding.prototype.setContext
*/
ClientContextBinding.prototype.setContext = function(oContext) {
var that = this;
if (this.oContext != oContext) {
this.oContext = oContext;
this.oModel.createBindingContext(this.sPath, this.oContext, this.mParameters, function(oContext) {
that.oElementContext = oContext;
that._fireChange();
});
}
};
return ClientContextBinding;
}, /* bExport= */ true);
},
"sap/ui/model/ClientListBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the JSON model implementation of a list binding
sap.ui.define(['jquery.sap.global', './FilterType', './ListBinding', './FilterProcessor', './SorterProcessor'],
function(jQuery, FilterType, ListBinding, FilterProcessor, SorterProcessor) {
"use strict";
/**
*
* @class
* List binding implementation for client models
*
* @param {sap.ui.model.Model} oModel
* @param {string} sPath
* @param {sap.ui.model.Context} oContext
* @param {sap.ui.model.Sorter|sap.ui.model.Sorter[]} [aSorters] initial sort order (can be either a sorter or an array of sorters)
* @param {sap.ui.model.Filter|sap.ui.model.Filter[]} [aFilters] predefined filter/s (can be either a filter or an array of filters)
* @param {object} [mParameters]
*
* @alias sap.ui.model.ClientListBinding
* @extends sap.ui.model.ListBinding
*/
var ClientListBinding = ListBinding.extend("sap.ui.model.ClientListBinding", /** @lends sap.ui.model.ClientListBinding.prototype */ {
constructor : function(oModel, sPath, oContext, aSorters, aFilters, mParameters){
ListBinding.apply(this, arguments);
this.bIgnoreSuspend = false;
this.update();
},
metadata : {
publicMethods : [
"getLength"
]
}
});
/**
* Return contexts for the list or a specified subset of contexts
* @param {int} [iStartIndex=0] the startIndex where to start the retrieval of contexts
* @param {int} [iLength=length of the list] determines how many contexts to retrieve beginning from the start index.
* Default is the whole list length.
*
* @return {Array} the contexts array
* @private
*/
ClientListBinding.prototype._getContexts = function(iStartIndex, iLength) {
if (!iStartIndex) {
iStartIndex = 0;
}
if (!iLength) {
iLength = Math.min(this.iLength, this.oModel.iSizeLimit);
}
var iEndIndex = Math.min(iStartIndex + iLength, this.aIndices.length),
oContext,
aContexts = [],
sPrefix = this.oModel.resolve(this.sPath, this.oContext);
if (sPrefix && !jQuery.sap.endsWith(sPrefix, "/")) {
sPrefix += "/";
}
for (var i = iStartIndex; i < iEndIndex; i++) {
oContext = this.oModel.getContext(sPrefix + this.aIndices[i]);
aContexts.push(oContext);
}
return aContexts;
};
/**
* Setter for context
* @param {Object} oContext the new context object
*/
ClientListBinding.prototype.setContext = function(oContext) {
if (this.oContext != oContext) {
this.oContext = oContext;
if (this.isRelative()) {
this.update();
this._fireChange({reason: sap.ui.model.ChangeReason.Context});
}
}
};
/**
* @see sap.ui.model.ListBinding.prototype.getLength
*
*/
ClientListBinding.prototype.getLength = function() {
return this.iLength;
};
/**
* Return the length of the list
*
* @return {int} the length
*/
ClientListBinding.prototype._getLength = function() {
return this.aIndices.length;
};
/**
* Get indices of the list
*/
ClientListBinding.prototype.updateIndices = function(){
this.aIndices = [];
for (var i = 0; i < this.oList.length; i++) {
this.aIndices.push(i);
}
};
/**
* @see sap.ui.model.ListBinding.prototype.sort
*/
ClientListBinding.prototype.sort = function(aSorters){
if (!aSorters) {
this.aSorters = null;
this.updateIndices();
this.applyFilter();
} else {
if (aSorters instanceof sap.ui.model.Sorter) {
aSorters = [aSorters];
}
this.aSorters = aSorters;
this.applySort();
}
this.bIgnoreSuspend = true;
this._fireChange({reason: sap.ui.model.ChangeReason.Sort});
// TODO remove this if the sorter event gets removed which is deprecated
this._fireSort({sorter: aSorters});
this.bIgnoreSuspend = false;
return this;
};
/**
* Sorts the list
* @private
*/
ClientListBinding.prototype.applySort = function(){
var that = this;
if (!this.aSorters || this.aSorters.length == 0) {
return;
}
this.aIndices = SorterProcessor.apply(this.aIndices, this.aSorters, function(vRef, sPath) {
return that.oModel.getProperty(sPath, that.oList[vRef]);
});
};
/**
* Filters the list.
*
* Filters are first grouped according to their binding path.
* All filters belonging to a group are ORed and after that the
* results of all groups are ANDed.
* Usually this means, all filters applied to a single table column
* are ORed, while filters on different table columns are ANDed.
*
* @param {sap.ui.model.Filter[]} aFilters Array of filter objects
* @param {sap.ui.model.FilterType} sFilterType Type of the filter which should be adjusted, if it is not given, the standard behaviour applies
* @return {sap.ui.model.ListBinding} returns <code>this</code> to facilitate method chaining
*
* @public
*/
ClientListBinding.prototype.filter = function(aFilters, sFilterType){
this.updateIndices();
if (aFilters instanceof sap.ui.model.Filter) {
aFilters = [aFilters];
}
if (sFilterType == FilterType.Application) {
this.aApplicationFilters = aFilters || [];
} else if (sFilterType == FilterType.Control) {
this.aFilters = aFilters || [];
} else {
//Previous behaviour
this.aFilters = aFilters || [];
this.aApplicationFilters = [];
}
aFilters = this.aFilters.concat(this.aApplicationFilters);
if (aFilters.length == 0) {
this.aFilters = [];
this.aApplicationFilters = [];
this.iLength = this._getLength();
} else {
this.applyFilter();
}
this.applySort();
this.bIgnoreSuspend = true;
this._fireChange({reason: sap.ui.model.ChangeReason.Filter});
// TODO remove this if the filter event gets removed which is deprecated
if (sFilterType == FilterType.Application) {
this._fireFilter({filters: this.aApplicationFilters});
} else {
this._fireFilter({filters: this.aFilters});
}
this.bIgnoreSuspend = false;
return this;
};
/**
* Filters the list
* Filters are first grouped according to their binding path.
* All filters belonging to a group are ORed and after that the
* results of all groups are ANDed.
* Usually this means, all filters applied to a single table column
* are ORed, while filters on different table columns are ANDed.
* Multiple MultiFilters are ORed.
*
* @private
*/
ClientListBinding.prototype.applyFilter = function(){
if (!this.aFilters) {
return;
}
var aFilters = this.aFilters.concat(this.aApplicationFilters),
that = this;
this.aIndices = FilterProcessor.apply(this.aIndices, aFilters, function(vRef, sPath) {
return that.oModel.getProperty(sPath, that.oList[vRef]);
});
this.iLength = this.aIndices.length;
};
/**
* Get distinct values
*
* @param {String} sPath
*
* @protected
*/
ClientListBinding.prototype.getDistinctValues = function(sPath){
var aResult = [],
oMap = {},
sValue,
that = this;
jQuery.each(this.oList, function(i, oContext) {
sValue = that.oModel.getProperty(sPath, oContext);
if (!oMap[sValue]) {
oMap[sValue] = true;
aResult.push(sValue);
}
});
return aResult;
};
return ClientListBinding;
}, /* bExport= */ true);
},
"sap/ui/model/ClientModel.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides client-based DataBinding implementation
sap.ui.define(['jquery.sap.global', './ClientContextBinding', './ClientListBinding', './ClientPropertyBinding', './ClientTreeBinding', './Model'],
function(jQuery, ClientContextBinding, ClientListBinding, ClientPropertyBinding, ClientTreeBinding, Model) {
"use strict";
/**
* Constructor for a new ClientModel.
*
* @class Model implementation for Client models
* @abstract
* @extends sap.ui.model.Model
*
* @author SAP SE
* @version 1.28.5
*
* @param {object} oData URL where to load the data from
* @constructor
* @public
* @alias sap.ui.model.ClientModel
*/
var ClientModel = Model.extend("sap.ui.model.ClientModel", /** @lends sap.ui.model.ClientModel.prototype */ {
constructor : function(oData) {
Model.apply(this, arguments);
this.bCache = true;
this.aPendingRequestHandles = [];
if (typeof oData == "string") {
this.loadData(oData);
}
},
metadata : {
publicMethods : ["loadData", "setData", "getData", "setProperty", "forceNoCache"]
}
});
/**
* Returns the current data of the model.
* Be aware that the returned object is a reference to the model data so all changes to that data will also change the model data.
*
* @return the data object
* @public
*/
ClientModel.prototype.getData = function(){
return this.oData;
};
/**
* @see sap.ui.model.Model.prototype.bindElement
*
*/
/**
* @see sap.ui.model.Model.prototype.createBindingContext
*
*/
ClientModel.prototype.createBindingContext = function(sPath, oContext, mParameters, fnCallBack) {
// optional parameter handling
if (typeof oContext == "function") {
fnCallBack = oContext;
oContext = null;
}
if (typeof mParameters == "function") {
fnCallBack = mParameters;
mParameters = null;
}
// resolve path and create context
var sContextPath = this.resolve(sPath, oContext),
oNewContext = (sContextPath == undefined) ? undefined : this.getContext(sContextPath ? sContextPath : "/");
if (!oNewContext) {
oNewContext = null;
}
if (fnCallBack) {
fnCallBack(oNewContext);
}
return oNewContext;
};
ClientModel.prototype._ajax = function(oParameters){
var that = this;
if (this.bDestroyed) {
return;
}
function wrapHandler(fn) {
return function() {
// request finished, remove request handle from pending request array
var iIndex = jQuery.inArray(oRequestHandle, that.aPendingRequestHandles);
if (iIndex > -1) {
that.aPendingRequestHandles.splice(iIndex, 1);
}
// call original handler method
if (!(oRequestHandle && oRequestHandle.bSuppressErrorHandlerCall)) {
fn.apply(this, arguments);
}
};
}
oParameters.success = wrapHandler(oParameters.success);
oParameters.error = wrapHandler(oParameters.error);
var oRequestHandle = jQuery.ajax(oParameters);
// add request handle to array and return it (only for async requests)
if (oParameters.async) {
this.aPendingRequestHandles.push(oRequestHandle);
}
};
/**
* @see sap.ui.model.Model.prototype.destroy
* @public
*/
ClientModel.prototype.destroy = function() {
Model.prototype.destroy.apply(this, arguments);
// Abort pending requests
if (this.aPendingRequestHandles) {
for (var i = this.aPendingRequestHandles.length - 1; i >= 0; i--) {
var oRequestHandle = this.aPendingRequestHandles[i];
if (oRequestHandle && oRequestHandle.abort) {
oRequestHandle.bSuppressErrorHandlerCall = true;
oRequestHandle.abort();
}
}
delete this.aPendingRequestHandles;
}
};
/**
* @see sap.ui.model.Model.prototype.destroyBindingContext
*
*/
ClientModel.prototype.destroyBindingContext = function(oContext) {
// TODO: what todo here?
};
/**
* @see sap.ui.model.Model.prototype.bindContext
*/
ClientModel.prototype.bindContext = function(sPath, oContext, mParameters) {
var oBinding = new ClientContextBinding(this, sPath, oContext, mParameters);
return oBinding;
};
/**
* update all bindings
* @param {boolean} bForceUpdate true/false: Default = false. If set to false an update
* will only be done when the value of a binding changed.
* @public
*/
ClientModel.prototype.updateBindings = function(bForceUpdate) {
this.checkUpdate(bForceUpdate);
};
/**
* Force no caching.
* @param {boolean} [bForceNoCache=false] whether to force not to cache
* @public
*/
ClientModel.prototype.forceNoCache = function(bForceNoCache) {
this.bCache = !bForceNoCache;
};
return ClientModel;
}, /* bExport= */ true);
},
"sap/ui/model/ClientPropertyBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the JSON model implementation of a property binding
sap.ui.define(['jquery.sap.global', './PropertyBinding'],
function(jQuery, PropertyBinding) {
"use strict";
/**
*
* @class
* Property binding implementation for client models
*
* @param {sap.ui.model.Model} oModel
* @param {String} sPath
* @param {sap.ui.model.Context} oContext
* @param {Object} [mParameters]
*
* @alias sap.ui.model.ClientPropertyBinding
* @extends sap.ui.model.PropertyBinding
*/
var ClientPropertyBinding = PropertyBinding.extend("sap.ui.model.ClientPropertyBinding", /** @lends sap.ui.model.ClientPropertyBinding.prototype */ {
constructor : function(oModel, sPath, oContext, mParameters){
PropertyBinding.apply(this, arguments);
this.oValue = this._getValue();
}
});
/**
* @see sap.ui.model.PropertyBinding.prototype.getValue
*/
ClientPropertyBinding.prototype.getValue = function(){
return this.oValue;
};
/**
* Returns the current value of the bound target (incl. re-evaluation)
* @return {object} the current value of the bound target
*/
ClientPropertyBinding.prototype._getValue = function(){
var sProperty = this.sPath.substr(this.sPath.lastIndexOf("/") + 1);
if (sProperty == "__name__") {
var aPath = this.oContext.split("/");
return aPath[aPath.length - 1];
}
return this.oModel.getProperty(this.sPath, this.oContext); // ensure to survive also not set model object
};
/**
* Setter for context
*/
ClientPropertyBinding.prototype.setContext = function(oContext) {
if (this.oContext != oContext) {
this.oContext = oContext;
if (this.isRelative()) {
this.checkUpdate();
}
}
};
return ClientPropertyBinding;
}, /* bExport= */ true);
},
"sap/ui/model/ClientTreeBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the JSON model implementation of a list binding
sap.ui.define(['jquery.sap.global', './TreeBinding'],
function(jQuery, TreeBinding) {
"use strict";
/**
*
* @class
* Tree binding implementation for client models
*
* @param {sap.ui.model.Model} oModel
* @param {string} sPath the path pointing to the tree / array that should be bound
* @param {object} [oContext=null] the context object for this databinding (optional)
* @param {array} [aFilters=null] predefined filter/s contained in an array (optional)
* @param {object} [mParameters=null] additional model specific parameters (optional)
* @alias sap.ui.model.ClientTreeBinding
* @extends sap.ui.model.TreeBinding
*/
var ClientTreeBinding = TreeBinding.extend("sap.ui.model.ClientTreeBinding", /** @lends sap.ui.model.ClientTreeBinding.prototype */ {
constructor : function(oModel, sPath, oContext, aFilters, mParameters){
TreeBinding.apply(this, arguments);
if (!this.oContext) {
this.oContext = "";
}
this.filterInfo = {};
this.filterInfo.aFilteredContexts = [];
this.filterInfo.oParentContext = {};
if (this.aFilters) {
if (this.oModel._getObject(this.sPath, this.oContext)) {
this.filter(aFilters);
}
}
}
});
/**
* Return root contexts for the tree
*
* @return {object[]} the contexts array
* @protected
* @param {integer} iStartIndex the startIndex where to start the retrieval of contexts
* @param {integer} iLength determines how many contexts to retrieve beginning from the start index.
*/
ClientTreeBinding.prototype.getRootContexts = function(iStartIndex, iLength) {
if (!iStartIndex) {
iStartIndex = 0;
}
if (!iLength) {
iLength = this.oModel.iSizeLimit;
}
var aContexts = [];
if (!this.oModel.isList(this.sPath)) {
var oContext = this.oModel.getContext(this.sPath);
if (this.bDisplayRootNode) {
aContexts = [oContext];
} else {
aContexts = this.getNodeContexts(oContext);
}
} else {
var that = this;
jQuery.each(this.oModel._getObject(this.sPath), function(iIndex, oObject) {
that._saveSubContext(oObject, aContexts, that.sPath + (jQuery.sap.endsWith(that.sPath, "/") ? "" : "/"), iIndex);
});
}
return aContexts.slice(iStartIndex, iStartIndex + iLength);
};
/**
* Return node contexts for the tree
* @param {object} oContext to use for retrieving the node contexts
* @param {integer} iStartIndex the startIndex where to start the retrieval of contexts
* @param {integer} iLength determines how many contexts to retrieve beginning from the start index.
* @return {object[]} the contexts array
* @protected
*/
ClientTreeBinding.prototype.getNodeContexts = function(oContext, iStartIndex, iLength) {
if (!iStartIndex) {
iStartIndex = 0;
}
if (!iLength) {
iLength = this.oModel.iSizeLimit;
}
var sContextPath = oContext.getPath();
if (!jQuery.sap.endsWith(sContextPath,"/")) {
sContextPath = sContextPath + "/";
}
if (!jQuery.sap.startsWith(sContextPath,"/")) {
sContextPath = "/" + sContextPath;
}
var aContexts = [],
that = this,
oNode = this.oModel._getObject(sContextPath),
aArrayNames = this.mParameters && this.mParameters.arrayNames,
aChildArray;
if (aArrayNames && jQuery.isArray(aArrayNames)) {
jQuery.each(aArrayNames, function(iIndex, sArrayName){
aChildArray = oNode[sArrayName];
if (aChildArray) {
jQuery.each(aChildArray, function(sSubName, oSubChild) {
that._saveSubContext(oSubChild, aContexts, sContextPath, sArrayName + "/" + sSubName);
});
}
});
} else {
if (oNode) {
jQuery.sap.each(oNode, function(sName, oChild) {
if (jQuery.isArray(oChild)) {
jQuery.each(oChild, function(sSubName, oSubChild) {
that._saveSubContext(oSubChild, aContexts, sContextPath, sName + "/" + sSubName);
});
} else if (typeof oChild == "object") {
that._saveSubContext(oChild, aContexts, sContextPath, sName);
}
});
}
}
return aContexts.slice(iStartIndex, iStartIndex + iLength);
};
/**
* Returns if the node has child nodes
*
* @param {object} oContext the context element of the node
* @return {boolean} true if node has children
*
* @public
*/
ClientTreeBinding.prototype.hasChildren = function(oContext) {
return oContext ? this.getNodeContexts(oContext).length > 0 : false;
};
ClientTreeBinding.prototype._saveSubContext = function(oNode, aContexts, sContextPath, sName) {
if (typeof oNode == "object") {
var oNodeContext = this.oModel.getContext(sContextPath + sName);
// check if there is a filter on this level applied
if (this.aFilters && !this.bIsFiltering) {
if (jQuery.inArray(oNodeContext, this.filterInfo.aFilteredContexts) != -1) {
aContexts.push(oNodeContext);
}
} else {
aContexts.push(oNodeContext);
}
}
};
/**
* Filters the tree according to the filter definitions.
*
* The filtering is applied recursively through the tree.
* The parent nodes of filtered child nodes will also be displayed if they don't match the filter conditions.
* All filters belonging to a group (=have the same path) are ORed and after that the
* results of all groups are ANDed.
*
* @see sap.ui.model.TreeBinding.prototype.filter
* @param {sap.ui.model.Filter[]} aFilters Array of filter objects
* @public
*/
ClientTreeBinding.prototype.filter = function(aFilters){
// The filtering is applied recursively through the tree and stores all filtered contexts and its parent contexts in an array.
// reset previous stored filter contexts
this.filterInfo.aFilteredContexts = [];
this.filterInfo.oParentContext = {};
if (!aFilters || !jQuery.isArray(aFilters) || aFilters.length == 0) {
this.aFilters = null;
} else {
this.aFilters = aFilters;
// start with binding path root
var oContext = new sap.ui.model.Context(this.oModel, this.sPath);
this.filterRecursive(oContext);
}
this._fireChange({reason: "filter"});
// TODO remove this if the filter event is removed
this._fireFilter({filters: aFilters});
};
/**
* filters the tree recursively.
* @param {object} oParentContext the context where to start. The children of this node context are then filtered recursively.
* @private
*/
ClientTreeBinding.prototype.filterRecursive = function(oParentContext){
this.bIsFiltering = true;
var aChildren = this.getNodeContexts(oParentContext);
this.bIsFiltering = false;
if (aChildren.length > 0) {
var that = this;
jQuery.each(aChildren, function(i, oChildContext){
that.filterRecursive(oChildContext);
});
this.applyFilter(oParentContext);
}
};
/**
* Performs the real filtering and stores all filtered contexts and its parent context into an array.
* @param {object} oParentContext the context where to start. The children of this node context are filtered.
* @private
*/
ClientTreeBinding.prototype.applyFilter = function(oParentContext){
if (!this.aFilters) {
return;
}
var that = this,
oFilterGroups = {},
aFilterGroup,
aFiltered = [],
bGroupFiltered = false,
bFiltered = true;
this.bIsFiltering = true;
var aUnfilteredContexts = this.getNodeContexts(oParentContext);
this.bIsFiltering = false;
jQuery.each(that.aFilters, function(j, oFilter) {
if (oFilter.sPath) {
aFilterGroup = oFilterGroups[oFilter.sPath];
if (!aFilterGroup) {
aFilterGroup = oFilterGroups[oFilter.sPath] = [];
}
} else {
aFilterGroup = oFilterGroups["__multiFilter"];
if (!aFilterGroup) {
aFilterGroup = oFilterGroups["__multiFilter"] = [];
}
}
aFilterGroup.push(oFilter);
});
jQuery.each(aUnfilteredContexts, function(i, oUnfilteredContext) {
bFiltered = true;
jQuery.each(oFilterGroups, function(sPath, aFilterGroup) {
if (sPath !== "__multiFilter") {
var oValue = that.oModel._getObject(sPath, oUnfilteredContext);
if (typeof oValue == "string") {
oValue = oValue.toUpperCase();
}
bGroupFiltered = false;
jQuery.each(aFilterGroup, function(j, oFilter) {
var fnTest = that.getFilterFunction(oFilter);
if (oValue != undefined && fnTest(oValue)) {
bGroupFiltered = true;
return false;
}
});
} else {
bGroupFiltered = false;
jQuery.each(aFilterGroup, function(j, oFilter) {
bGroupFiltered = that._resolveMultiFilter(oFilter, oUnfilteredContext);
if (bGroupFiltered) {
return false;
}
});
}
if (!bGroupFiltered) {
bFiltered = false;
return false;
}
});
if (bFiltered) {
aFiltered.push(oUnfilteredContext);
}
});
if (aFiltered.length > 0) {
jQuery.merge(this.filterInfo.aFilteredContexts, aFiltered);
this.filterInfo.aFilteredContexts.push(oParentContext);
this.filterInfo.oParentContext = oParentContext;
}
// push additionally parentcontexts if any children are already included in filtered contexts
if (jQuery.inArray(this.filterInfo.oParentContext, aUnfilteredContexts) != -1) {
this.filterInfo.aFilteredContexts.push(oParentContext);
// set the parent context which was added to be the new parent context
this.filterInfo.oParentContext = oParentContext;
}
};
/**
* Resolve the client list binding and check if an index matches
*
* @private
*/
ClientTreeBinding.prototype._resolveMultiFilter = function(oMultiFilter, oUnfilteredContext){
var that = this,
bMatched = false,
aFilters = oMultiFilter.aFilters;
if (aFilters) {
jQuery.each(aFilters, function(i, oFilter) {
var bLocalMatch = false;
if (oFilter._bMultiFilter) {
bLocalMatch = that._resolveMultiFilter(oFilter, oUnfilteredContext);
} else if (oFilter.sPath) {
var oValue = that.oModel.getProperty(oFilter.sPath, oUnfilteredContext);
if (typeof oValue == "string") {
oValue = oValue.toUpperCase();
}
var fnTest = that.getFilterFunction(oFilter);
if (oValue != undefined && fnTest(oValue)) {
bLocalMatch = true;
}
}
if (bLocalMatch && oMultiFilter.bAnd) {
bMatched = true;
} else if (!bLocalMatch && oMultiFilter.bAnd) {
bMatched = false;
return false;
} else if (bLocalMatch) {
bMatched = true;
return false;
}
});
}
return bMatched;
};
/**
* Provides a JS filter function for the given filter
* @private
*/
ClientTreeBinding.prototype.getFilterFunction = function(oFilter){
if (oFilter.fnTest) {
return oFilter.fnTest;
}
var oValue1 = oFilter.oValue1,
oValue2 = oFilter.oValue2;
if (typeof oValue1 == "string") {
oValue1 = oValue1.toUpperCase();
}
if (typeof oValue2 == "string") {
oValue2 = oValue2.toUpperCase();
}
switch (oFilter.sOperator) {
case "EQ":
oFilter.fnTest = function(value) { return value == oValue1; }; break;
case "NE":
oFilter.fnTest = function(value) { return value != oValue1; }; break;
case "LT":
oFilter.fnTest = function(value) { return value < oValue1; }; break;
case "LE":
oFilter.fnTest = function(value) { return value <= oValue1; }; break;
case "GT":
oFilter.fnTest = function(value) { return value > oValue1; }; break;
case "GE":
oFilter.fnTest = function(value) { return value >= oValue1; }; break;
case "BT":
oFilter.fnTest = function(value) { return (value > oValue1) && (value < oValue2); }; break;
case "Contains":
oFilter.fnTest = function(value) { return value.indexOf(oValue1) != -1; }; break;
case "StartsWith":
oFilter.fnTest = function(value) { return value.indexOf(oValue1) == 0; }; break;
case "EndsWith":
oFilter.fnTest = function(value) { return value.indexOf(oValue1) == value.length - new String(oFilter.oValue1).length; }; break;
default:
oFilter.fnTest = function(value) { return true; };
}
return oFilter.fnTest;
};
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} bForceupdate
*
*/
ClientTreeBinding.prototype.checkUpdate = function(bForceupdate){
this._fireChange();
};
return ClientTreeBinding;
}, /* bExport= */ true);
},
"sap/ui/model/CompositeBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstract property binding.
sap.ui.define(['jquery.sap.global', './PropertyBinding', './CompositeType'],
function(jQuery, PropertyBinding, CompositeType) {
"use strict";
/**
* Constructor for CompositeBinding
*
* @class
* The CompositeBinding is used to bundle multiple property bindings which are be used to provide a single binding against
* these property bindings.
*
* @public
* @alias sap.ui.model.CompositeBinding
*/
var CompositeBinding = PropertyBinding.extend("sap.ui.model.CompositeBinding", /** @lends sap.ui.model.CompositeBinding.prototype */ {
constructor : function (aBindings, bRawValues) {
PropertyBinding.apply(this, [null,""]);
this.aBindings = aBindings;
this.aValues = null;
this.bRawValues = bRawValues;
},
metadata : {
publicMethods : [
"getBindings", "attachChange", "detachChange"
]
}
});
CompositeBinding.prototype.getPath = function() {
jQuery.sap.assert(null, "Composite Binding has no path!");
return null;
};
CompositeBinding.prototype.getModel = function() {
jQuery.sap.assert(null, "Composite Binding has no model!");
return null;
};
CompositeBinding.prototype.getContext = function() {
jQuery.sap.assert(null, "Composite Binding has no context!");
return null;
};
/**
* Sets the optional type and internal type for the binding. The type and internal type are used to do the parsing/formatting correctly.
* The internal type is the property type of the element which the value is formatted to.
*
* @param {sap.ui.model.CompositeType} oType the type for the binding
* @param {String} sInternalType the internal type of the element property which this binding is bound against.
*
* @public
*/
CompositeBinding.prototype.setType = function(oType, sInternalType) {
if (oType && !(oType instanceof CompositeType)) {
throw new Error("Only CompositeType can be used as type for composite bindings!")
}
PropertyBinding.prototype.setType.apply(this, arguments);
// If a composite type is used, the type decides whether to use raw values or not
if (this.oType) {
this.bRawValues = this.oType.getUseRawValues();
}
};
/**
* sets the context for each property binding in this composite binding
* @param {object} oContext the new context for the bindings
*/
CompositeBinding.prototype.setContext = function(oContext) {
jQuery.each(this.aBindings, function(i, oBinding){
// null context could also be set
if (!oContext || oBinding.updateRequired(oContext.getModel())) {
oBinding.setContext(oContext);
}
});
};
/**
* Sets the values. This will cause the setValue to be called for each nested binding, except
* for undefined values in the array.
*
* @param {array} aValues the values to set for this binding
*
* @public
*/
CompositeBinding.prototype.setValue = function(aValues) {
var oValue;
jQuery.each(this.aBindings, function(i, oBinding) {
oValue = aValues[i];
if (oValue !== undefined) {
oBinding.setValue(oValue);
}
});
};
/**
* Returns the raw values of the property bindings in an array.
*
* @return {object} the values of the internal property bindings in an array
*
* @public
*/
CompositeBinding.prototype.getValue = function() {
var aValues = [],
oValue;
jQuery.each(this.aBindings, function(i, oBinding) {
oValue = oBinding.getValue();
aValues.push(oValue);
});
return aValues;
};
/**
* Sets the external value of a composite binding. If no CompositeType is assigned to the binding, the default
* implementation assumes a space separated list of values. This will cause the setValue to be called for each
* nested binding, except for undefined values in the array.
*
* @param {object} oValue the value to set for this binding
*
* @public
*/
CompositeBinding.prototype.setExternalValue = function(oValue) {
var aValues, aCurrentValues;
// No twoway binding when using formatters
if (this.fnFormatter) {
jQuery.sap.log.warning("Tried to use twoway binding, but a formatter function is used");
return;
}
if (this.oType) {
if (this.oType.getParseWithValues()) {
aCurrentValues = [];
if (this.bRawValues) {
aCurrentValues = this.getValue();
} else {
jQuery.each(this.aBindings, function(i, oBinding) {
aCurrentValues.push(oBinding.getExternalValue());
});
}
}
aValues = this.oType.parseValue(oValue, this.sInternalType, aCurrentValues);
} else {
// default: multiple values are split by space character together if no formatter or type specified
if (typeof oValue == "string") {
aValues = oValue.split(" ");
} else {
aValues = [oValue];
}
}
if (this.bRawValues) {
this.setValue(aValues);
} else {
jQuery.each(this.aBindings, function(i, oBinding) {
oValue = aValues[i];
if (oValue !== undefined) {
oBinding.setExternalValue(oValue);
}
});
}
};
/**
* Returns the current external value of the bound target which is formatted via a type or formatter function.
*
* @return {object} the current value of the bound target
*
*@throws sap.ui.model.FormatException
*
* @public
*/
CompositeBinding.prototype.getExternalValue = function() {
var aValues = [],
oValue;
if (this.bRawValues) {
aValues = this.getValue();
} else {
jQuery.each(this.aBindings, function(i, oBinding) {
aValues.push(oBinding.getExternalValue());
});
}
if (this.fnFormatter) {
oValue = this.fnFormatter.apply(this, aValues);
} else if (this.oType) {
oValue = this.oType.formatValue(aValues, this.sInternalType);
} else {
if ( aValues.length > 1) {
// default: multiple values are joined together as space separated list if no formatter or type specified
oValue = aValues.join(" ");
} else {
oValue = aValues[0];
}
}
return oValue;
};
/**
* Returns the property bindings contained in this composite binding.
*
* @return {array} the property bindings in this composite binding
*
* @public
*/
CompositeBinding.prototype.getBindings = function() {
return this.aBindings;
};
//Eventing and related
/**
* Attach event-handler <code>fnFunction</code> to the '_change' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
CompositeBinding.prototype.attachChange = function(fnFunction, oListener) {
this.attachEvent("change", fnFunction, oListener);
if (this.aBindings) {
var that = this;
jQuery.each(this.aBindings, function(i,oBinding) {
oBinding.attachChange(that.checkUpdate, that);
});
}
};
/**
* Detach event-handler <code>fnFunction</code> from the '_change' event of this <code>sap.ui.model.Model</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
*/
CompositeBinding.prototype.detachChange = function(fnFunction, oListener) {
this.detachEvent("change", fnFunction, oListener);
if (this.aBindings) {
var that = this;
jQuery.each(this.aBindings, function(i,oBinding) {
oBinding.detachChange(that.checkUpdate, that);
});
}
};
/**
* Determines if the property bindings in the composite binding should be updated by calling updateRequired on all property bindings with the specified model.
* @param {object} oModel The model instance to compare against
* @returns {boolean} true if this binding should be updated
* @protected
*/
CompositeBinding.prototype.updateRequired = function(oModel) {
var bUpdateRequired = false;
jQuery.each(this.aBindings, function(i, oBinding){
bUpdateRequired = bUpdateRequired || oBinding.updateRequired(oModel);
});
return bUpdateRequired;
};
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} bForceupdate
*
*/
CompositeBinding.prototype.checkUpdate = function(bForceupdate){
var aValues = this.getValue();
if (!jQuery.sap.equal(aValues, this.aValues) || bForceupdate) {// optimize for not firing the events when unneeded
this.aValues = aValues;
this._fireChange({reason: sap.ui.model.ChangeReason.Change});
}
};
return CompositeBinding;
}, /* bExport= */ true);
},
"sap/ui/model/CompositeType.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base implementation for all model implementations
sap.ui.define(['jquery.sap.global', './FormatException', './ParseException', './SimpleType', './ValidateException'],
function(jQuery, FormatException, ParseException, SimpleType, ValidateException) {
"use strict";
/**
* Constructor for a new CompositeType.
*
* @class
* This is an abstract base class for composite types. Composite types have multiple input values and know
* how to merge/split them upon formatting/parsing the value. Typical use case a currency or amount values.
*
* Subclasses of CompositeTypes can define boolean properties in the constructor:
* - bUseRawValues: the format and parse method will handle raw model values, types of embedded bindings are ignored
* - bParseWithValues: the parse method call will include the current binding values as a third parameter
*
* @abstract
*
* @extends sap.ui.model.SimpleType
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @param {object} [oFormatOptions] options as provided by concrete subclasses
* @param {object} [oConstraints] constraints as supported by concrete subclasses
* @public
* @alias sap.ui.model.CompositeType
*/
var CompositeType = SimpleType.extend("sap.ui.model.CompositeType", /** @lends sap.ui.model.CompositeType.prototype */ {
constructor : function(oFormatOptions, oConstraints) {
SimpleType.apply(this, arguments);
this.sName = "CompositeType";
this.bUseRawValues = false;
this.bParseWithValues = false;
},
metadata : {
"abstract" : true,
publicMethods : []
}
});
/**
* Format the given set of values in model representation to an output value in the given
* internal type. This happens according to the format options, if target type is 'string'.
* If aValues is not defined or null, null will be returned.
*
* @function
* @name sap.ui.model.CompositeType.prototype.formatValue
* @param {array} aValues the values to be formatted
* @param {string} sInternalType the target type
* @return {any} the formatted output value
*
* @public
*/
/**
* Parse a value of an internal type to the expected set of values of the model type.
*
* @function
* @name sap.ui.model.CompositeType.prototype.parseValue
* @param {any} oValue the value to be parsed
* @param {string} sInternalType the source type
* @param {array} aCurrentValues the current values of all binding parts
* @return {array} the parse result array
*
* @public
*/
/**
* Validate whether a given value in model representation is valid and meets the
* defined constraints (if any).
*
* @function
* @name sap.ui.model.CompositeType.prototype.validateValue
* @param {array} aValues the set of values to be validated
*
* @public
*/
/**
* Returns whether this composite types works on raw values or formatted values
*/
CompositeType.prototype.getUseRawValues = function() {
return this.bUseRawValues;
};
/**
* Returns whether this composite types needs current values for parsing
*/
CompositeType.prototype.getParseWithValues = function() {
return this.bParseWithValues;
};
return CompositeType;
}, /* bExport= */ true);
},
"sap/ui/model/Context.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for model bindings
sap.ui.define(['jquery.sap.global', 'sap/ui/base/EventProvider'],
function(jQuery, EventProvider) {
"use strict";
/**
* Constructor for Context class.
*
* @class
* The Context is a pointer to an object in the model data, which is used to
* allow definition of relative bindings, which are resolved relative to the
* defined object.
* Context elements are created either by the ListBinding for each list entry
* or by using createBindingContext.
*
* @param {sap.ui.model.Model} oModel the model
* @param {String} sPath the path
* @param {Object} oContext the context object
* @abstract
* @public
* @alias sap.ui.model.Context
*/
var Context = sap.ui.base.Object.extend("sap.ui.model.Context", /** @lends sap.ui.model.Context.prototype */ {
constructor : function(oModel, sPath){
sap.ui.base.Object.apply(this);
this.oModel = oModel;
this.sPath = sPath;
},
metadata : {
"abstract" : true,
publicMethods : [
"getModel", "getPath", "getProperty", "getObject"
]
}
});
// Getter
/**
* Getter for model
* @public
* @return {sap.ui.core.Model} the model
*/
Context.prototype.getModel = function() {
return this.oModel;
};
/**
* Getter for path of the context itself or a subpath
* @public
* @param {String} sPath the binding path
* @return {String} the binding path
*/
Context.prototype.getPath = function(sPath) {
return this.sPath + (sPath ? "/" + sPath : "");
};
/**
* Gets the property with the given relative binding path
* @public
* @param {String} sPath the binding path
* @return {any} the property value
*/
Context.prototype.getProperty = function(sPath) {
return this.oModel.getProperty(sPath, this);
};
/**
* Gets the (model dependent) object the context points to or the object with the given relative binding path
* @public
* @param {String} sPath the binding path
* @return {object} the context object
*/
Context.prototype.getObject = function(sPath) {
return this.oModel.getObject(sPath, this);
};
/**
* toString method returns path for compatbility
*/
Context.prototype.toString = function() {
return this.sPath;
};
return Context;
}, /* bExport= */ true);
},
"sap/ui/model/ContextBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for list bindings
sap.ui.define(['jquery.sap.global', './Binding'],
function(jQuery, Binding) {
"use strict";
/**
* Constructor for ContextBinding
*
* @class
* The ContextBinding is a specific binding for a setting context for the model
*
* @param {sap.ui.model.Model} oModel
* @param {String} sPath
* @param {Object} oContext
* @param {Object} [mParameters]
* @param {Object} [oEvents] object defining event handlers
* @abstract
* @public
* @alias sap.ui.model.ContextBinding
*/
var ContextBinding = Binding.extend("sap.ui.model.ContextBinding", /** @lends sap.ui.model.ContextBinding.prototype */ {
constructor : function(oModel, sPath, oContext, mParameters, oEvents){
Binding.call(this, oModel, sPath, oContext, mParameters, oEvents);
this.oElementContext = null;
this.bInitial = true;
},
metadata : {
publicMethods : [
// methods
"getElementContext"
]
}
});
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} bForceupdate
*/
ContextBinding.prototype.checkUpdate = function(bForceupdate) {
// nothing to do here, data changes can not change the context
};
/**
* Refreshes the binding, check whether the model data has been changed and fire change event
* if this is the case. For server side models this should refetch the data from the server.
*
* @param {boolean} bForceUpdate Does not have any effect on this binding
*
* @public
*/
/**
* Return the bound context
*/
ContextBinding.prototype.getBoundContext = function(oContext) {
return this.oElementContext;
};
return ContextBinding;
}, /* bExport= */ true);
},
"sap/ui/model/Filter.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a filter for list bindings
sap.ui.define(['jquery.sap.global', './FilterOperator', 'sap/ui/core/util/UnicodeNormalizer'],
function(jQuery, FilterOperator, UnicodeNormalizer) {
"use strict";
/**
* Constructor for Filter
* You can either pass an object with the filter parameters or use the function arguments
*
* Using object:
* new sap.ui.model.Filter({
* path: "...",
* operator: "...",
* value1: "...",
* value2: "..."
* })
*
* OR:
* new sap.ui.model.Filter({
* path: "...",
* test: function(oValue) {
* }
* })
*
* OR:
* new sap.ui.model.Filter({
* filters: [...],
* and: true|false
* })
*
* You can only pass sPath, sOperator and their values OR sPath, fnTest OR aFilters and bAnd. You will get an error if you define an invalid combination of filters parameters.
*
* Using arguments:
* new sap.ui.model.Filter(sPath, sOperator, oValue1, oValue2);
* OR
* new sap.uji.model.Filter(sPath, fnTest);
* OR
* new sap.ui.model.Filter(aFilters, bAnd);
*
* aFilters is an array of other instances of sap.ui.model.Filter. If bAnd is set all filters within the filter will be ANDed else they will be ORed.
*
* @class
* Filter for the list binding
*
* @param {object} oFilterInfo the filter info object
* @param {string} oFilterInfo.path the binding path for this filter
* @param {function} oFilterInfo.test function which is used to filter the items which should return a boolean value to indicate whether the current item is preserved
* @param {sap.ui.model.FilterOperator} oFilterInfo.operator operator used for the filter
* @param {object} oFilterInfo.value1 first value to use for filter
* @param {object} [oFilterInfo.value2=null] fecond value to use for filter
* @param {array} oFilterInfo.filters array of filters on which logical conjunction is applied
* @param {boolean} oFilterInfo.and indicates whether an "and" logical conjunction is applied on the filters. If it's set to false, an "or" conjunction is applied
* @public
* @alias sap.ui.model.Filter
*/
var Filter = sap.ui.base.Object.extend("sap.ui.model.Filter", /** @lends sap.ui.model.Filter.prototype */ {
constructor : function(sPath, sOperator, oValue1, oValue2){
//There are two different ways of specifying a filter
//If can be passed in only one object or defined with parameters
if (typeof sPath === "object" && !jQuery.isArray(sPath)) {
var oFilterData = sPath;
this.sPath = oFilterData.path;
this.sOperator = oFilterData.operator;
this.oValue1 = oFilterData.value1;
this.oValue2 = oFilterData.value2;
this.aFilters = oFilterData.filters || oFilterData.aFilters;
this.bAnd = oFilterData.and || oFilterData.bAnd;
this.fnTest = oFilterData.test;
} else {
//If parameters are used we have to check whether a regular or a multi filter is specified
if (jQuery.isArray(sPath)) {
this.aFilters = sPath;
} else {
this.sPath = sPath;
}
if (jQuery.type(sOperator) === "boolean") {
this.bAnd = sOperator;
} else if (jQuery.type(sOperator) === "function" ) {
this.fnTest = sOperator;
} else {
this.sOperator = sOperator;
}
this.oValue1 = oValue1;
this.oValue2 = oValue2;
}
this.oValue1 = this._normalizeValue(this.oValue1);
this.oValue2 = this._normalizeValue(this.oValue2);
if (jQuery.isArray(this.aFilters) && !this.sPath && !this.sOperator && !this.oValue1 && !this.oValue2) {
this._bMultiFilter = true;
jQuery.each(this.aFilters, function(iIndex, oFilter) {
if (!(oFilter instanceof Filter)) {
jQuery.sap.log.error("Filter in Aggregation of Multi filter has to be instance of sap.ui.model.Filter");
}
});
} else if (!this.aFilters && this.sPath !== undefined && ((this.sOperator && this.oValue1 !== undefined) || this.fnTest)) {
this._bMultiFilter = false;
} else {
jQuery.sap.log.error("Wrong parameters defined for filter.");
}
}
});
/**
* Normalizes the filtered value if it is a String and the function is defined.
*
* @param {object} oValue the value to be filtered.
* @private
*/
Filter.prototype._normalizeValue = function(oValue) {
if (typeof oValue === "string" && String.prototype.normalize != undefined) {
oValue = oValue.normalize();
}
return oValue;
};
return Filter;
}, /* bExport= */ true);
},
"sap/ui/model/FilterOperator.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides enumeration sap.ui.model.FilterOperator
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Operators for the Filter.
*
* @namespace
* @public
* @alias sap.ui.model.FilterOperator
*/
var FilterOperator = {
/**
* FilterOperator equals
* @public
*/
EQ: "EQ",
/**
* FilterOperator not equals
* @public
*/
NE: "NE",
/**
* FilterOperator less than
* @public
*/
LT: "LT",
/**
* FilterOperator less or equals
* @public
*/
LE: "LE",
/**
* FilterOperator greater than
* @public
*/
GT: "GT",
/**
* FilterOperator greater or equals
* @public
*/
GE: "GE",
/**
* FilterOperator between.
* When used on strings, the BT operator might not behave intuitively. For example,
* when filtering a list of Names with BT "A", "B", all Names starting with "A" will be
* included as well as the name "B" itself, but no other name starting with "B".
* @public
*/
BT: "BT",
/**
* FilterOperator contains
* @public
*/
Contains: "Contains",
/**
* FilterOperator starts with
* @public
*/
StartsWith: "StartsWith",
/**
* FilterOperator ends with
* @public
*/
EndsWith: "EndsWith"
};
return FilterOperator;
}, /* bExport= */ true);
},
"sap/ui/model/FilterProcessor.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Clientside Filter processor
* @namespace sap.ui.model.FilterProcessor
*/
var FilterProcessor = {};
/**
* Filters the list
* Filters are first grouped according to their binding path.
* All filters belonging to a group are ORed and after that the
* results of all groups are ANDed.
* Usually this means, all filters applied to a single table column
* are ORed, while filters on different table columns are ANDed.
* Multiple MultiFilters are ORed.
*
* @param {array} aData the data array to be filtered
* @param {array} aFilters the filter array
* @param {function} fnGetValue the method to get the actual value to filter on
*
* @public
*/
FilterProcessor.apply = function(aData, aFilters, fnGetValue){
if (!aFilters || aFilters.length == 0) {
return aData;
}
var that = this,
oFilterGroups = {},
aFilterGroup,
aFiltered = [],
bGroupFiltered = false,
bFiltered = true;
jQuery.each(aFilters, function(j, oFilter) {
if (oFilter.sPath !== undefined) {
aFilterGroup = oFilterGroups[oFilter.sPath];
if (!aFilterGroup) {
aFilterGroup = oFilterGroups[oFilter.sPath] = [];
}
} else {
aFilterGroup = oFilterGroups["__multiFilter"];
if (!aFilterGroup) {
aFilterGroup = oFilterGroups["__multiFilter"] = [];
}
}
aFilterGroup.push(oFilter);
});
jQuery.each(aData, function(i, vRef) {
bFiltered = true;
jQuery.each(oFilterGroups, function(sPath, aFilterGroup) {
if (sPath !== "__multiFilter") {
var oValue = fnGetValue(vRef, sPath);
oValue = that.normalizeFilterValue(oValue);
bGroupFiltered = false;
jQuery.each(aFilterGroup, function(j, oFilter) {
var fnTest = that.getFilterFunction(oFilter);
if (oValue != undefined && fnTest(oValue)) {
bGroupFiltered = true;
return false;
}
});
} else {
bGroupFiltered = false;
jQuery.each(aFilterGroup, function(j, oFilter) {
bGroupFiltered = that._resolveMultiFilter(oFilter, vRef, fnGetValue);
if (bGroupFiltered) {
return false;
}
});
}
if (!bGroupFiltered) {
bFiltered = false;
return false;
}
});
if (bFiltered) {
aFiltered.push(vRef);
}
});
return aFiltered;
};
/**
* Normalize filter value
*
* @private
*/
FilterProcessor.normalizeFilterValue = function(oValue){
if (typeof oValue == "string") {
return oValue.toUpperCase();
}
if (oValue instanceof Date) {
return oValue.getTime();
}
return oValue;
};
/**
* Resolve the client list binding and check if an index matches
*
* @private
*/
FilterProcessor._resolveMultiFilter = function(oMultiFilter, vRef, fnGetValue){
var that = this,
bMatched = false,
aFilters = oMultiFilter.aFilters;
if (aFilters) {
jQuery.each(aFilters, function(i, oFilter) {
var bLocalMatch = false;
if (oFilter._bMultiFilter) {
bLocalMatch = that._resolveMultiFilter(oFilter, vRef, fnGetValue);
} else if (oFilter.sPath !== undefined) {
var oValue = fnGetValue(vRef, oFilter.sPath);
oValue = that.normalizeFilterValue(oValue);
var fnTest = that.getFilterFunction(oFilter);
if (oValue != undefined && fnTest(oValue)) {
bLocalMatch = true;
}
}
if (bLocalMatch && oMultiFilter.bAnd) {
bMatched = true;
} else if (!bLocalMatch && oMultiFilter.bAnd) {
bMatched = false;
return false;
} else if (bLocalMatch) {
bMatched = true;
return false;
}
});
}
return bMatched;
};
/**
* Provides a JS filter function for the given filter
*/
FilterProcessor.getFilterFunction = function(oFilter){
if (oFilter.fnTest) {
return oFilter.fnTest;
}
var oValue1 = this.normalizeFilterValue(oFilter.oValue1),
oValue2 = this.normalizeFilterValue(oFilter.oValue2);
switch (oFilter.sOperator) {
case "EQ":
oFilter.fnTest = function(value) { return value == oValue1; }; break;
case "NE":
oFilter.fnTest = function(value) { return value != oValue1; }; break;
case "LT":
oFilter.fnTest = function(value) { return value < oValue1; }; break;
case "LE":
oFilter.fnTest = function(value) { return value <= oValue1; }; break;
case "GT":
oFilter.fnTest = function(value) { return value > oValue1; }; break;
case "GE":
oFilter.fnTest = function(value) { return value >= oValue1; }; break;
case "BT":
oFilter.fnTest = function(value) { return (value >= oValue1) && (value <= oValue2); }; break;
case "Contains":
oFilter.fnTest = function(value) {
if (typeof value != "string") {
throw new Error("Only \"String\" values are supported for the FilterOperator: \"Contains\".");
}
return value.indexOf(oValue1) != -1;
};
break;
case "StartsWith":
oFilter.fnTest = function(value) {
if (typeof value != "string") {
throw new Error("Only \"String\" values are supported for the FilterOperator: \"StartsWith\".");
}
return value.indexOf(oValue1) == 0;
};
break;
case "EndsWith":
oFilter.fnTest = function(value) {
if (typeof value != "string") {
throw new Error("Only \"String\" values are supported for the FilterOperator: \"EndsWith\".");
}
var iPos = value.lastIndexOf(oValue1);
if (iPos == -1) {
return false;
}
return iPos == value.length - new String(oFilter.oValue1).length;
};
break;
default:
oFilter.fnTest = function(value) { return true; };
}
return oFilter.fnTest;
};
return FilterProcessor;
});
},
"sap/ui/model/FilterType.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides enumeration sap.ui.model.FilterType
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Operators for the Filter.
*
* @namespace
* @public
* @alias sap.ui.model.FilterType
*/
var FilterType = {
/**
* Filters which are changed by the application
* @public
*/
Application: "Application",
/**
* Filters which are set by the different controls
* @public
*/
Control: "Control"
};
return FilterType;
}, /* bExport= */ true);
},
"sap/ui/model/FormatException.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a filter for list bindings
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Exception'],
function(jQuery, Exception) {
"use strict";
/**
* FormatException class
*
* This exception is thrown, when an error occurs while trying to convert a value of the model to
* a specific property value in the UI.
*
* @alias sap.ui.model.FormatException
*/
var FormatException = function(message) {
this.name = "FormatException";
this.message = message;
};
FormatException.prototype = jQuery.sap.newObject(Exception.prototype);
return FormatException;
}, /* bExport= */ true);
},
"sap/ui/model/ListBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for list bindings
sap.ui.define(['jquery.sap.global', './Binding', './Filter', './Sorter'],
function(jQuery, Binding, Filter, Sorter) {
"use strict";
/**
* Constructor for ListBinding
*
* @class
* The ListBinding is a specific binding for lists in the model, which can be used
* to populate Tables or ItemLists.
*
* @param {sap.ui.model.Model} oModel
* @param {string} sPath
* @param {sap.ui.model.Context} oContext
* @param {array} [aSorters] initial sort order (can be either a sorter or an array of sorters)
* @param {array} [aFilters] predefined filter/s (can be either a filter or an array of filters)
* @param {object} [mParameters]
*
* @public
* @alias sap.ui.model.ListBinding
*/
var ListBinding = Binding.extend("sap.ui.model.ListBinding", /** @lends sap.ui.model.ListBinding.prototype */ {
constructor : function(oModel, sPath, oContext, aSorters, aFilters, mParameters){
Binding.call(this, oModel, sPath, oContext, mParameters);
this.aSorters = aSorters;
if (!jQuery.isArray(this.aSorters) && this.aSorters instanceof Sorter) {
this.aSorters = [this.aSorters];
} else if (!jQuery.isArray(this.aSorters)) {
this.aSorters = [];
}
this.aFilters = [];
if (!jQuery.isArray(aFilters) && aFilters instanceof Filter) {
aFilters = [aFilters];
} else if (!jQuery.isArray(aFilters)) {
aFilters = [];
}
this.aApplicationFilters = aFilters;
this.bUseExtendedChangeDetection = false;
},
metadata : {
"abstract" : true,
publicMethods : [
// methods
"getContexts", "sort", "attachSort", "detachSort", "filter", "attachFilter", "detachFilter", "getDistinctValues", "isGrouped", "getLength", "isLengthFinal"
]
}
});
// the 'abstract methods' to be implemented by child classes
/**
* Returns the current value of the bound target
*
* @function
* @name sap.ui.model.ListBinding.prototype.getContexts
* @return {sap.ui.model.Context[]} the array of contexts for each row of the bound list
*
* @public
*/
/**
* Filters the list according to the filter definitions
*
* @function
* @name sap.ui.model.ListBinding.prototype.filter
* @param {object[]} aFilters Array of filter objects
* @param {sap.ui.model.FilterType} sFilterType Type of the filter which should be adjusted, if it is not given, the standard behaviour applies
* @return {sap.ui.model.ListBinding} returns <code>this</code> to facilitate method chaining
*
* @public
*/
/**
* Sorts the list according to the sorter object
*
* @function
* @name sap.ui.model.ListBinding.prototype.sort
* @param {sap.ui.model.Sorter|Array} aSorters the Sorter object or an array of sorters which defines the sort order
* @return {sap.ui.model.ListBinding} returns <code>this</code> to facilitate method chaining
* @public
*/
/**
* Returns the number of entries in the list. This might be an estimated or preliminary length, in case
* the full length is not known yet, see method isLengthFinal().
*
* @return {int} returns the number of entries in the list
* @since 1.24
* @public
*/
ListBinding.prototype.getLength = function() {
return 0;
};
/**
* Returns whether the length which can be retrieved using getLength() is a known, final length,
* or an preliminary or estimated length which may change if further data is requested.
*
* @return {boolean} returns whether the length is final
* @since 1.24
* @public
*/
ListBinding.prototype.isLengthFinal = function() {
return true;
};
// base methods, may be overridden by child classes
/**
* Returns list of distinct values for the given relative binding path
*
* @param {string} sPath the relative binding path
* @return {Array} the array of distinct values.
*
* @public
*/
ListBinding.prototype.getDistinctValues = function(sPath) {
return null;
};
//Eventing and related
/**
* Attach event-handler <code>fnFunction</code> to the 'sort' event of this <code>sap.ui.model.ListBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event. It now contains a parameter (reason : "sort") when a sorter event is fired.
*/
ListBinding.prototype.attachSort = function(fnFunction, oListener) {
this.attachEvent("sort", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'sort' event of this <code>sap.ui.model.ListBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event.
*/
ListBinding.prototype.detachSort = function(fnFunction, oListener) {
this.detachEvent("sort", fnFunction, oListener);
};
/**
* Fire event _sort to attached listeners.
* @param {Map} [mArguments] the arguments to pass along with the event.
* @private
* @deprecated use the change event. It now contains a parameter (reason : "sort") when a sorter event is fired.
*/
ListBinding.prototype._fireSort = function(mArguments) {
this.fireEvent("sort", mArguments);
};
/**
* Attach event-handler <code>fnFunction</code> to the 'filter' event of this <code>sap.ui.model.ListBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event. It now contains a parameter (reason : "filter") when a filter event is fired.
*/
ListBinding.prototype.attachFilter = function(fnFunction, oListener) {
this.attachEvent("filter", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the 'filter' event of this <code>sap.ui.model.ListBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event.
*/
ListBinding.prototype.detachFilter = function(fnFunction, oListener) {
this.detachEvent("filter", fnFunction, oListener);
};
/**
* Fire event _filter to attached listeners.
* @param {Map} [mArguments] the arguments to pass along with the event.
* @private
* @deprecated use the change event. It now contains a parameter (reason : "filter") when a filter event is fired.
*/
ListBinding.prototype._fireFilter = function(mArguments) {
this.fireEvent("filter", mArguments);
};
/**
* Checks if grouping is enabled for the binding<br/>
* @public
*/
ListBinding.prototype.isGrouped = function() {
return this.aSorters.length > 0 && !!this.aSorters[0].fnGroup;
};
/**
* Enable extended change detection
* @private
*/
ListBinding.prototype.enableExtendedChangeDetection = function( ) {
this.bUseExtendedChangeDetection = true;
if (this.update) {
this.update();
}
};
return ListBinding;
}, /* bExport= */ true);
},
"sap/ui/model/Model.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base implementation for all model implementations
sap.ui.define(['jquery.sap.global', 'sap/ui/core/message/MessageProcessor', './BindingMode', './Context'],
function(jQuery, MessageProcessor, BindingMode, Context) {
"use strict";
/**
* The SAPUI5 Data Binding API.
*
* The default binding mode for model implementations (if not implemented otherwise) is two way and the supported binding modes by the model
* are one way, two way and one time. The default binding mode can be changed by the application for each model instance.
* A model implementation should specify its supported binding modes and set the default binding mode accordingly
* (e.g. if the model supports only one way binding the default binding mode should also be set to one way).
*
* This MessageProcessor is able to handle Messages with the normal binding syntax as target.
*
* @namespace
* @name sap.ui.model
* @public
*/
/**
* Constructor for a new Model.
*
* @class
* This is an abstract base class for model objects.
* @abstract
*
* @extends sap.ui.core.message.MessageProcessor
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @public
* @alias sap.ui.model.Model
*/
var Model = MessageProcessor.extend("sap.ui.model.Model", /** @lends sap.ui.model.Model.prototype */ {
constructor : function () {
MessageProcessor.apply(this, arguments);
this.oData = {};
this.bDestroyed = false;
this.aBindings = [];
this.mContexts = {};
this.iSizeLimit = 100;
this.sDefaultBindingMode = BindingMode.TwoWay;
this.mSupportedBindingModes = {"OneWay": true, "TwoWay": true, "OneTime": true};
this.bLegacySyntax = false;
this.sUpdateTimer = null;
},
metadata : {
"abstract" : true,
publicMethods : [
// methods
"bindProperty", "bindList", "bindTree", "bindContext", "createBindingContext", "destroyBindingContext", "getProperty",
"getDefaultBindingMode", "setDefaultBindingMode", "isBindingModeSupported", "attachParseError", "detachParseError",
"attachRequestCompleted", "detachRequestCompleted", "attachRequestFailed", "detachRequestFailed", "attachRequestSent",
"detachRequestSent", "setSizeLimit", "refresh", "isList", "getObject"
]
/* the following would save code, but requires the new ManagedObject (1.9.1)
, events : {
"parseError" : {},
"requestFailed" : {},
"requestSent" : {},
"requestCompleted" ; {}
}
*/
}
});
/**
* Map of event names, that are provided by the model.
*/
Model.M_EVENTS = {
/**
* Depending on the model implementation a ParseError should be fired if a parse error occurred.
* Contains the parameters:
* errorCode, url, reason, srcText, line, linepos, filepos
*/
ParseError : "parseError",
/**
* Depending on the model implementation a RequestFailed should be fired if a request to a backend failed.
* Contains the parameters:
* message, statusCode, statusText and responseText
*
*/
RequestFailed : "requestFailed",
/**
* Depending on the model implementation a RequestSent should be fired when a request to a backend is sent.
* Contains Parameters: url, type, async, info (<strong>deprecated</strong>), infoObject
*
*/
RequestSent : "requestSent",
/**
* Depending on the model implementation a RequestCompleted should be fired when a request to a backend is completed regardless if the request failed or succeeded.
* Contains Parameters: url, type, async, info (<strong>deprecated</strong>), infoObject, success, errorobject
*
*/
RequestCompleted : "requestCompleted"
};
/**
* The 'requestFailed' event is fired, when data retrieval from a backend failed.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.Model#requestFailed
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {string} oControlEvent.getParameters.message A text that describes the failure.
* @param {string} oControlEvent.getParameters.statusCode HTTP status code returned by the request (if available)
* @param {string} oControlEvent.getParameters.statusText The status as a text, details not specified, intended only for diagnosis output
* @param {string} [oControlEvent.getParameters.responseText] Response that has been received for the request ,as a text string
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the 'requestFailed' event of this <code>sap.ui.model.Model</code>.<br/>
*
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event.
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, this Model is used.
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.attachRequestFailed = function(oData, fnFunction, oListener) {
this.attachEvent("requestFailed", oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'requestFailed' event of this <code>sap.ui.model.Model</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.detachRequestFailed = function(fnFunction, oListener) {
this.detachEvent("requestFailed", fnFunction, oListener);
return this;
};
/**
* Fire event requestFailed to attached listeners.
*
* @param {object} [mArguments] the arguments to pass along with the event.
* @param {string} [mArguments.message] A text that describes the failure.
* @param {string} [mArguments.statusCode] HTTP status code returned by the request (if available)
* @param {string} [mArguments.statusText] The status as a text, details not specified, intended only for diagnosis output
* @param {string} [mArguments.responseText] Response that has been received for the request ,as a text string
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @protected
*/
Model.prototype.fireRequestFailed = function(mArguments) {
this.fireEvent("requestFailed", mArguments);
return this;
};
/**
* The 'parseError' event is fired when parsing of a model document (e.g. XML response) fails.
*
* @name sap.ui.model.Model#parseError
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {int} oControlEvent.getParameters.errorCode
* @param {string} oControlEvent.getParameters.url
* @param {string} oControlEvent.getParameters.reason
* @param {string} oControlEvent.getParameters.srcText
* @param {int} oControlEvent.getParameters.line
* @param {int} oControlEvent.getParameters.linepos
* @param {int} oControlEvent.getParameters.filepos
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the 'parseError' event of this <code>sap.ui.model.Model</code>.<br/>
*
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event.
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, the global context (window) is used.
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.attachParseError = function(oData, fnFunction, oListener) {
this.attachEvent("parseError", oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'parseError' event of this <code>sap.ui.model.Model</code>.<br/>
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.detachParseError = function(fnFunction, oListener) {
this.detachEvent("parseError", fnFunction, oListener);
return this;
};
/**
* Fire event parseError to attached listeners.
*
* @param {object} [mArguments] the arguments to pass along with the event.
* @param {int} [mArguments.errorCode]
* @param {string} [mArguments.url]
* @param {string} [mArguments.reason]
* @param {string} [mArguments.srcText]
* @param {int} [mArguments.line]
* @param {int} [mArguments.linepos]
* @param {int} [mArguments.filepos]
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @protected
*/
Model.prototype.fireParseError = function(mArguments) {
this.fireEvent("parseError", mArguments);
return this;
};
/**
* The 'requestSent' event is fired, after a request has been sent to a backend.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.Model#requestSent
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {string} oControlEvent.getParameters.url The url which is sent to the backend
* @param {string} [oControlEvent.getParameters.type] The type of the request (if available)
* @param {boolean} [oControlEvent.getParameters.async] If the request is synchronous or asynchronous (if available)
* @param {string} [oControlEvent.getParameters.info] Additional information for the request (if available) <strong>deprecated</strong>
* @param {object} [oControlEvent.getParameters.infoObject] Additional information for the request (if available)
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the 'requestSent' event of this <code>sap.ui.model.Model</code>.
*
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event.
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, the global context (window) is used.
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.attachRequestSent = function(oData, fnFunction, oListener) {
this.attachEvent("requestSent", oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'requestSent' event of this <code>sap.ui.model.Model</code>.
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.detachRequestSent = function(fnFunction, oListener) {
this.detachEvent("requestSent", fnFunction, oListener);
return this;
};
/**
* Fire event requestSent to attached listeners.
*
* @param {object} [mArguments] the arguments to pass along with the event.
* @param {string} [mArguments.url] The url which is sent to the backend.
* @param {string} [mArguments.type] The type of the request (if available)
* @param {boolean} [mArguments.async] If the request is synchronous or asynchronous (if available)
* @param {string} [mArguments.info] additional information for the request (if available) <strong>deprecated</strong>
* @param {object} [mArguments.infoObject] Additional information for the request (if available)
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @protected
*/
Model.prototype.fireRequestSent = function(mArguments) {
this.fireEvent("requestSent", mArguments);
return this;
};
/**
* The 'requestCompleted' event is fired, after a request has been completed (includes receiving a response),
* no matter whether the request succeeded or not.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.Model#requestCompleted
* @event
* @param {sap.ui.base.Event} oControlEvent
* @param {sap.ui.base.EventProvider} oControlEvent.getSource
* @param {object} oControlEvent.getParameters
* @param {string} oControlEvent.getParameters.url The url which was sent to the backend
* @param {string} [oControlEvent.getParameters.type] The type of the request (if available)
* @param {boolean} oControlEvent.getParameters.success if the request has been successful or not. In case of errors consult the optional errorobject parameter.
* @param {object} [oControlEvent.getParameters.errorobject] If the request failed the error if any can be accessed in this property.
* @param {boolean} [oControlEvent.getParameters.async] If the request is synchronous or asynchronous (if available)
* @param {string} [oControlEvent.getParameters.info] Additional information for the request (if available) <strong>deprecated</strong>
* @param {object} [oControlEvent.getParameters.infoObject] Additional information for the request (if available)
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the 'requestCompleted' event of this <code>sap.ui.model.Model</code>.
*
*
* @param {object}
* [oData] The object, that should be passed along with the event-object when firing the event.
* @param {function}
* fnFunction The function to call, when the event occurs. This function will be called on the
* oListener-instance (if present) or in a 'static way'.
* @param {object}
* [oListener] Object on which to call the given function. If empty, the global context (window) is used.
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.attachRequestCompleted = function(oData, fnFunction, oListener) {
this.attachEvent("requestCompleted", oData, fnFunction, oListener);
return this;
};
/**
* Detach event-handler <code>fnFunction</code> from the 'requestCompleted' event of this <code>sap.ui.model.Model</code>.
*
* The passed function and listener object must match the ones previously used for event registration.
*
* @param {function}
* fnFunction The function to call, when the event occurs.
* @param {object}
* oListener Object on which the given function had to be called.
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @public
*/
Model.prototype.detachRequestCompleted = function(fnFunction, oListener) {
this.detachEvent("requestCompleted", fnFunction, oListener);
return this;
};
/**
* Fire event requestCompleted to attached listeners.
*
* @param {object} [mArguments] the arguments to pass along with the event.
* @param {string} [mArguments.url] The url which was sent to the backend.
* @param {string} [mArguments.type] The type of the request (if available)
* @param {boolean} [mArguments.async] If the request was synchronous or asynchronous (if available)
* @param {string} [mArguments.info] additional information for the request (if available) <strong>deprecated</strong>
* @param {object} [mArguments.infoObject] Additional information for the request (if available)
*
* @return {sap.ui.model.Model} <code>this</code> to allow method chaining
* @protected
*/
Model.prototype.fireRequestCompleted = function(mArguments) {
this.fireEvent("requestCompleted", mArguments);
return this;
};
Model.prototype.attachMessageChange = function(oData, fnFunction, oListener) {
this.attachEvent("messageChange", oData, fnFunction, oListener);
return this;
};
Model.prototype.detachMessageChange = function(fnFunction, oListener) {
this.detachEvent("messageChange", fnFunction, oListener);
return this;
};
// the 'abstract methods' to be implemented by child classes
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.bindProperty
* @function
* @param {string}
* sPath the path pointing to the property that should be bound
* @param {object}
* [oContext=null] the context object for this databinding (optional)
* @param {object}
* [mParameters=null] additional model specific parameters (optional)
* @return {sap.ui.model.PropertyBinding}
*
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.bindList
* @function
* @param {string}
* sPath the path pointing to the list / array that should be bound
* @param {object}
* [oContext=null] the context object for this databinding (optional)
* @param {sap.ui.model.Sorter}
* [aSorters=null] initial sort order (can be either a sorter or an array of sorters) (optional)
* @param {array}
* [aFilters=null] predefined filter/s (can be either a filter or an array of filters) (optional)
* @param {object}
* [mParameters=null] additional model specific parameters (optional)
* @return {sap.ui.model.ListBinding}
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.bindTree
* @function
* @param {string}
* sPath the path pointing to the tree / array that should be bound
* @param {object}
* [oContext=null] the context object for this databinding (optional)
* @param {array}
* [aFilters=null] predefined filter/s contained in an array (optional)
* @param {object}
* [mParameters=null] additional model specific parameters (optional)
* @return {sap.ui.model.TreeBinding}
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.createBindingContext
* @function
* @param {string}
* sPath the path to create the new context from
* @param {object}
* [oContext=null] the context which should be used to create the new binding context
* @param {object}
* [mParameters=null] the parameters used to create the new binding context
* @param {function}
* [fnCallBack] the function which should be called after the binding context has been created
* @param {boolean}
* [bReload] force reload even if data is already available. For server side models this should
* refetch the data from the server
* @return {sap.ui.model.Context} the binding context, if it could be created synchronously
*
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.destroyBindingContext
* @function
* @param {object}
* oContext to destroy
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @name sap.ui.model.Model.prototype.getProperty
* @function
* @param {string}
* sPath the path to where to read the attribute value
* @param {object}
* [oContext=null] the context with which the path should be resolved
* @public
*/
/**
* Implement in inheriting classes
* @abstract
*
* @param {string}
* sPath the path to where to read the object
* @param {object}
* [oContext=null] the context with which the path should be resolved
* @public
*/
Model.prototype.getObject = function(sPath, oContext) {
return this.getProperty(sPath, oContext);
};
/**
* Create ContextBinding
* @abstract
*
* @name sap.ui.model.Model.prototype.bindContext
* @function
* @param {string | object}
* sPath the path pointing to the property that should be bound or an object
* which contains the following parameter properties: path, context, parameters
* @param {object}
* [oContext=null] the context object for this databinding (optional)
* @param {object}
* [mParameters=null] additional model specific parameters (optional)
* @param {object}
* [oEvents=null] event handlers can be passed to the binding ({change:myHandler})
* @return {sap.ui.model.ContextBinding}
*
* @public
*/
/**
* Gets a binding context. If context already exists, return it from the map,
* otherwise create one using the context constructor.
*
* @param {string} sPath the path
*/
Model.prototype.getContext = function(sPath) {
if (!jQuery.sap.startsWith(sPath, "/")) {
throw new Error("Path " + sPath + " must start with a / ");
}
var oContext = this.mContexts[sPath];
if (!oContext) {
oContext = new Context(this, sPath);
this.mContexts[sPath] = oContext;
}
return oContext;
};
/**
* Resolve the path relative to the given context.
*
* If a relative path is given (not starting with a '/') but no context,
* then the path can't be resolved and undefined is returned.
*
* For backward compatibility, the behavior of this method can be changed by
* setting the 'legacySyntax' property. Then an unresolvable, relative path
* is automatically converted into an absolute path.
*
* @param {string} sPath path to resolve
* @param {sap.ui.core.Context} [oContext] context to resolve a relative path against
* @return {string} resolved path or undefined
*/
Model.prototype.resolve = function(sPath, oContext) {
var bIsRelative = typeof sPath == "string" && !jQuery.sap.startsWith(sPath, "/"),
sResolvedPath = sPath,
sContextPath;
if (bIsRelative) {
if (oContext) {
sContextPath = oContext.getPath();
sResolvedPath = sContextPath + (jQuery.sap.endsWith(sContextPath, "/") ? "" : "/") + sPath;
} else {
sResolvedPath = this.isLegacySyntax() ? "/" + sPath : undefined;
}
}
// invariant: path never ends with a slash ... if root is requested we return /
if (sResolvedPath && sResolvedPath !== "/" && jQuery.sap.endsWith(sResolvedPath, "/")) {
sResolvedPath = sResolvedPath.substr(0, sResolvedPath.length - 1);
}
return sResolvedPath;
};
/**
* Add a binding to this model
*
* @param {sap.ui.model.Binding} oBinding the binding to be added
*/
Model.prototype.addBinding = function(oBinding) {
this.aBindings.push(oBinding);
};
/**
* Remove a binding from the model
*
* @param {sap.ui.model.Binding} oBinding the binding to be removed
*/
Model.prototype.removeBinding = function(oBinding) {
for (var i = 0; i < this.aBindings.length; i++) {
if (this.aBindings[i] == oBinding) {
this.aBindings.splice(i, 1);
break;
}
}
};
/**
* Get the default binding mode for the model
*
* @return {sap.ui.model.BindingMode} default binding mode of the model
*
* @public
*/
Model.prototype.getDefaultBindingMode = function() {
return this.sDefaultBindingMode;
};
/**
* Set the default binding mode for the model. If the default binding mode should be changed,
* this method should be called directly after model instance creation and before any binding creation.
* Otherwise it is not guaranteed that the existing bindings will be updated with the new binding mode.
*
* @param {sap.ui.model.BindingMode} sMode the default binding mode to set for the model
*
* @public
*/
Model.prototype.setDefaultBindingMode = function(sMode) {
if (this.isBindingModeSupported(sMode)) {
this.sDefaultBindingMode = sMode;
} else {
throw new Error("Binding mode " + sMode + " is not supported by this model.");
}
};
/**
* Check if the specified binding mode is supported by the model.
*
* @param {sap.ui.model.BindingMode} sMode the binding mode to check
*
* @public
*/
Model.prototype.isBindingModeSupported = function(sMode) {
return (sMode in this.mSupportedBindingModes);
};
/**
* Enables legacy path syntax handling
*
* This defines, whether relative bindings, which do not have a defined
* binding context, should be compatible to earlier releases which means
* they are resolved relative to the root element or handled strict and
* stay unresolved until a binding context is set
*
* @param {boolean} bLegacySyntax the path syntax to use
*
* @public
*/
Model.prototype.setLegacySyntax = function(bLegacySyntax) {
this.bLegacySyntax = bLegacySyntax;
};
/**
* Returns whether legacy path syntax is used
*
* @return {boolean}
*
* @public
*/
Model.prototype.isLegacySyntax = function() {
return this.bLegacySyntax;
};
/**
* Set the maximum number of entries which are used for for list bindings.
* @param {int} iSizeLimit collection size limit
* @public
*/
Model.prototype.setSizeLimit = function(iSizeLimit) {
this.iSizeLimit = iSizeLimit;
};
/**
* Override getInterface method to avoid creating an Interface object for models
*/
Model.prototype.getInterface = function() {
return this;
};
/**
* Refresh the model.
* This will check all bindings for updated data and update the controls if data has been changed.
*
* @param {boolean} bForceUpdate Update controls even if data has not been changed
* @public
*/
Model.prototype.refresh = function(bForceUpdate) {
this.checkUpdate(bForceUpdate);
if (bForceUpdate) {
this.fireMessageChange({oldMessages: this.mMessages});
}
};
/**
* Private method iterating the registered bindings of this model instance and initiating their check for update
* @param {boolean} bForceUpdate
* @param {boolean} bAsync
* @private
*/
Model.prototype.checkUpdate = function(bForceUpdate, bAsync) {
if (bAsync) {
if (!this.sUpdateTimer) {
this.sUpdateTimer = jQuery.sap.delayedCall(0, this, function() {
this.checkUpdate(bForceUpdate);
});
}
return;
}
if (this.sUpdateTimer) {
jQuery.sap.clearDelayedCall(this.sUpdateTimer);
this.sUpdateTimer = null;
}
var aBindings = this.aBindings.slice(0);
jQuery.each(aBindings, function(iIndex, oBinding) {
oBinding.checkUpdate(bForceUpdate);
});
};
/**
* Sets messages
*
* @param {object} mMessages Messages for this model
* @public
*/
Model.prototype.setMessages = function(mMessages) {
this.mMessages = mMessages || {};
this.checkMessages();
};
/**
* Get messages for path
*
* @param {string} sPath The binding path
* @protected
*/
Model.prototype.getMessagesByPath = function(sPath) {
return this.mMessages[sPath];
};
/**
* Private method iterating the registered bindings of this model instance and initiating their check for messages
* @private
*/
Model.prototype.checkMessages = function() {
var aBindings = this.aBindings.slice(0);
jQuery.each(aBindings, function(iIndex, oBinding) {
oBinding.checkMessages();
});
};
/**
* Destroys the model and clears the model data.
* A model implementation may override this function and perform model specific cleanup tasks e.g.
* abort requests, prevent new requests, etc.
*
* @see sap.ui.base.Object.prototype.destroy
* @public
*/
Model.prototype.destroy = function() {
MessageProcessor.prototype.destroy.apply(this, arguments);
this.oData = {};
this.aBindings = [];
this.mContexts = {};
if (this.sUpdateTimer) {
jQuery.sap.clearDelayedCall(this.sUpdateTimer);
}
this.bDestroyed = true;
};
/**
* Returns the meta model associated with this model if it is available for the concrete
* model type.
* @abstract
* @public
* @returns {sap.ui.model.MetaModel} The meta model or undefined if no meta model exists.
*/
Model.prototype.getMetaModel = function() {
return undefined;
};
return Model;
}, /* bExport= */ true);
},
"sap/ui/model/ParseException.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a filter for list bindings
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Exception'],
function(jQuery, Exception) {
"use strict";
/**
* ParseException class
*
* This exception is thrown, when a parse error occurs while converting a
* string value to a specific property type in the model.
* @alias sap.ui.model.ParseException
*/
var ParseException = function(message) {
this.name = "ParseException";
this.message = message;
};
ParseException.prototype = jQuery.sap.newObject(Exception.prototype);
return ParseException;
}, /* bExport= */ true);
},
"sap/ui/model/PropertyBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstract property binding.
sap.ui.define(['jquery.sap.global', './Binding', './SimpleType'],
function(jQuery, Binding, SimpleType) {
"use strict";
/**
* Constructor for PropertyBinding
*
* @class
* The PropertyBinding is used to access single data values in the data model.
*
* @param {sap.ui.model.Model} oModel
* @param {string} sPath
* @param {sap.ui.model.Context} oContext
* @param {object} [mParameters]
*
* @public
* @alias sap.ui.model.PropertyBinding
*/
var PropertyBinding = Binding.extend("sap.ui.model.PropertyBinding", /** @lends sap.ui.model.PropertyBinding.prototype */ {
constructor : function (oModel, sPath, oContext, mParameters) {
Binding.apply(this, arguments);
},
metadata : {
"abstract" : true,
publicMethods : [
"getValue", "setValue", "setType", "getType", "setFormatter", "getFormatter", "getExternalValue", "setExternalValue", "getBindingMode"
]
}
});
// the 'abstract methods' to be implemented by child classes
/**
* Returns the current value of the bound target
*
* @function
* @name sap.ui.model.PropertyBinding.prototype.getValue
* @return {object} the current value of the bound target
*
* @public
*/
/**
* Sets the value for this binding. A model implementation should check if the current default binding mode permits
* setting the binding value and if so set the new value also in the model.
*
* @function
* @name sap.ui.model.PropertyBinding.prototype.setValue
* @param {object} oValue the value to set for this binding
*
* @public
*/
/**
* Returns the current external value of the bound target which is formatted via a type or formatter function.
*
* @throws sap.ui.model.FormatException
*
* @return {object} the current value of the bound target
*
* @public
*/
PropertyBinding.prototype.getExternalValue = function() {
var oValue = this.getValue();
if (this.oType) {
oValue = this.oType.formatValue(oValue, this.sInternalType);
}
if (this.fnFormatter) {
oValue = this.fnFormatter(oValue);
}
return oValue;
};
/**
* Sets the value for this binding. The value is parsed and validated against its type and then set to the binding.
* A model implementation should check if the current default binding mode permits
* setting the binding value and if so set the new value also in the model.
*
* @param {object} oValue the value to set for this binding
*
* @throws sap.ui.model.ParseException
* @throws sap.ui.model.ValidateException
*
* @public
*/
PropertyBinding.prototype.setExternalValue = function(oValue) {
// formatter doesn't support two way binding
if (this.fnFormatter) {
jQuery.sap.log.warning("Tried to use twoway binding, but a formatter function is used");
return;
}
if (this.oType) {
oValue = this.oType.parseValue(oValue, this.sInternalType);
this.oType.validateValue(oValue);
}
// if no type specified set value directly
this.setValue(oValue);
};
/**
* Sets the optional type and internal type for the binding. The type and internal type are used to do the parsing/formatting correctly.
* The internal type is the property type of the element which the value is formatted to.
*
* @param {sap.ui.model.Type} oType the type for the binding
* @param {String} sInternalType the internal type of the element property which this binding is bound against.
*
* @public
*/
PropertyBinding.prototype.setType = function(oType, sInternalType) {
this.oType = oType;
this.sInternalType = sInternalType;
};
/**
* Returns the type if any for the binding.
* @returns {sap.ui.model.Type} the binding type
* @public
*/
PropertyBinding.prototype.getType = function() {
return this.oType;
};
/**
* Sets the optional formatter function for the binding.
* @param {function} fnFormatter the formatter function for the binding
*
* @public
*/
PropertyBinding.prototype.setFormatter = function(fnFormatter) {
this.fnFormatter = fnFormatter;
};
/**
* Returns the formatter function
* @returns {Function} the formatter function
* @public
*/
PropertyBinding.prototype.getFormatter = function() {
return this.fnFormatter;
};
/**
* Returns the binding mode
* @returns {sap.ui.model.BindingMode} the binding mode
* @public
*/
PropertyBinding.prototype.getBindingMode = function() {
return this.sMode;
};
/**
* Sets the binding mode
* @param {sap.ui.model.BindingMode} sBindingMode the binding mode
* @protected
*/
PropertyBinding.prototype.setBindingMode = function(sBindingMode) {
this.sMode = sBindingMode;
};
return PropertyBinding;
}, /* bExport= */ true);
},
"sap/ui/model/SimpleType.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base implementation for all model implementations
sap.ui.define(['jquery.sap.global', 'sap/ui/base/DataType', './FormatException', './ParseException', './Type', './ValidateException'],
function(jQuery, DataType, FormatException, ParseException, Type, ValidateException) {
"use strict";
/**
* Constructor for a new SimpleType.
*
* @class
* This is an abstract base class for simple types.
* @abstract
*
* @extends sap.ui.model.Type
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @param {object} [oFormatOptions] options as provided by concrete subclasses
* @param {object} [oConstraints] constraints as supported by concrete subclasses
* @public
* @alias sap.ui.model.SimpleType
*/
var SimpleType = Type.extend("sap.ui.model.SimpleType", /** @lends sap.ui.model.SimpleType.prototype */ {
constructor : function(oFormatOptions, oConstraints) {
Type.apply(this, arguments);
this.setFormatOptions(oFormatOptions || {});
this.setConstraints(oConstraints || {});
this.sName = "SimpleType";
},
metadata : {
"abstract" : true,
publicMethods : [
"setConstraints", "setFormatOptions", "formatValue", "parseValue", "validateValue"
]
}
});
/**
* Format the given value in model representation to an output value in the given
* internal type. This happens according to the format options, if target type is 'string'.
* If oValue is not defined or null, null will be returned.
*
* @function
* @name sap.ui.model.SimpleType.prototype.formatValue
* @param {any} oValue the value to be formatted
* @param {string} sInternalType the target type
* @return {any} the formatted output value
*
* @public
*/
/**
* Parse a value of an internal type to the expected value of the model type.
*
* @function
* @name sap.ui.model.SimpleType.prototype.parseValue
* @param {any} oValue the value to be parsed
* @param {string} sInternalType the source type
* @return {any} the parse result
*
* @public
*/
/**
* Validate whether a given value in model representation is valid and meets the
* defined constraints (if any).
*
* @function
* @name sap.ui.model.SimpleType.prototype.validateValue
* @param {any} oValue the value to be validated
*
* @public
*/
/**
* Sets constraints for this type. This is meta information used when validating the
* value, to ensure it meets certain criteria, e.g. maximum length, minimal amount
*
* @param {object} oConstraints the constraints to set for this type
*/
SimpleType.prototype.setConstraints = function(oConstraints) {
this.oConstraints = oConstraints;
};
/**
* Set format options for this type. This is meta information used when formatting and
* parsing values, such as patterns for number and date formatting or maximum length
*
* @param {object} oFormatOptions the options to set for this type
*/
SimpleType.prototype.setFormatOptions = function(oFormatOptions) {
this.oFormatOptions = oFormatOptions;
};
/**
* Returns the primitive type name for the given internal type name
*
* @param {string} sInternalType the internal type name
* @return {string} the primitive type name
*/
SimpleType.prototype.getPrimitiveType = function(sInternalType) {
// Avoid dealing with type objects, unless really necessary
switch (sInternalType) {
case "any":
case "boolean":
case "int":
case "float":
case "string":
case "object":
return sInternalType;
default:
var oInternalType = DataType.getType(sInternalType);
return oInternalType && oInternalType.getPrimitiveType().getName();
}
};
return SimpleType;
}, /* bExport= */ true);
},
"sap/ui/model/Sorter.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the concept of a sorter for list bindings
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
*
* Constructor for Sorter
*
* @class
* Sorter for the list binding
* This object defines the sort order for the list binding.
*
*
* @param {String} sPath the binding path used for sorting
* @param {boolean} [bDescending=false] whether the sort order should be descending
* @param {boolean|function} vGroup configure grouping of the content, can either be true to enable grouping
* based on the raw model property value, or a function which calculates the group value out of the
* context (e.g. oContext.getProperty("date").getYear() for year grouping). The control needs to
* implement the grouping behaviour for the aggregation which you want to group.
* @public
* @alias sap.ui.model.Sorter
*/
var Sorter = sap.ui.base.Object.extend("sap.ui.model.Sorter", /** @lends sap.ui.model.Sorter.prototype */ {
constructor : function(sPath, bDescending, vGroup){
if (typeof sPath === "object") {
var oSorterData = sPath;
sPath = oSorterData.path;
bDescending = oSorterData.descending;
vGroup = oSorterData.group;
}
this.sPath = sPath;
// if a model separator is found in the path, extract model name
var iSeparatorPos = this.sPath.indexOf(">");
if (iSeparatorPos > 0) {
this.sPath = this.sPath.substr(iSeparatorPos + 1);
}
this.bDescending = bDescending;
this.vGroup = vGroup;
if (typeof vGroup == "boolean" && vGroup) {
this.fnGroup = function(oContext) {
return oContext.getProperty(this.sPath);
};
}
if (typeof vGroup == "function") {
this.fnGroup = vGroup;
}
}
});
return Sorter;
}, /* bExport= */ true);
},
"sap/ui/model/SorterProcessor.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define(['jquery.sap.global'],
function(jQuery) {
"use strict";
/**
* Clientside Sorter processor
* @namespace sap.ui.model.SorterProcessor
*/
var SorterProcessor = {};
/**
* Sorts the list
*
* Sorters are applied according to their order in the sorter array.
*
* @param {array} aData the data array to be sorted
* @param {array} aSorters the sorter array
* @param {function} fnGetValue the method to get the actual value use for sorting
* @public
*/
SorterProcessor.apply = function(aData, aSorters, fnGetValue){
var that = this,
aSortValues = [],
aCompareFunctions = [],
oValue,
oSorter;
if (!aSorters || aSorters.length == 0) {
return aData;
}
function fnCompare(a, b) {
if (a == b) {
return 0;
}
if (b == null) {
return -1;
}
if (a == null) {
return 1;
}
if (typeof a == "string" && typeof b == "string") {
return a.localeCompare(b);
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
for (var j = 0; j < aSorters.length; j++) {
oSorter = aSorters[j];
aCompareFunctions[j] = oSorter.fnCompare;
if (!aCompareFunctions[j]) {
aCompareFunctions[j] = fnCompare;
}
/*eslint-disable no-loop-func */
jQuery.each(aData, function(i, vRef) {
oValue = fnGetValue(vRef, oSorter.sPath);
if (typeof oValue == "string") {
oValue = oValue.toLocaleUpperCase();
}
if (!aSortValues[j]) {
aSortValues[j] = [];
}
aSortValues[j][vRef] = oValue;
});
/*eslint-enable no-loop-func */
}
aData.sort(function(a, b) {
var valueA = aSortValues[0][a],
valueB = aSortValues[0][b];
return that._applySortCompare(aSorters, a, b, valueA, valueB, aSortValues, aCompareFunctions, 0);
});
return aData;
};
SorterProcessor._applySortCompare = function(aSorters, a, b, valueA, valueB, aSortValues, aCompareFunctions, iDepth){
var oSorter = aSorters[iDepth],
fnCompare = aCompareFunctions[iDepth],
returnValue;
returnValue = fnCompare(valueA, valueB);
if (oSorter.bDescending) {
returnValue = -returnValue;
}
if (returnValue == 0 && aSorters[iDepth + 1]) {
valueA = aSortValues[iDepth + 1][a];
valueB = aSortValues[iDepth + 1][b];
returnValue = this._applySortCompare(aSorters, a, b, valueA, valueB, aSortValues, aCompareFunctions, iDepth + 1);
}
return returnValue;
};
return SorterProcessor;
});
},
"sap/ui/model/TreeBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides an abstraction for list bindings
sap.ui.define(['jquery.sap.global', './Binding'],
function(jQuery, Binding) {
"use strict";
/**
* Constructor for TreeBinding
*
* @class
* The TreeBinding is a specific binding for trees in the model, which can be used
* to populate Trees.
*
* @param {sap.ui.model.Model} oModel
* @param {string}
* sPath the path pointing to the tree / array that should be bound
* @param {object}
* [oContext=null] the context object for this databinding (optional)
* @param {array}
* [aFilters=null] predefined filter/s contained in an array (optional)
* @param {object}
* [mParameters=null] additional model specific parameters (optional)
* @public
* @alias sap.ui.model.TreeBinding
*/
var TreeBinding = Binding.extend("sap.ui.model.TreeBinding", /** @lends sap.ui.model.TreeBinding.prototype */ {
constructor : function(oModel, sPath, oContext, aFilters, mParameters){
Binding.call(this, oModel, sPath, oContext, mParameters);
this.aFilters = aFilters;
this.bDisplayRootNode = mParameters && mParameters.displayRootNode === true;
},
metadata : {
"abstract" : true,
publicMethods : [
"getRootContexts", "getNodeContexts", "hasChildren", "filter"
]
}
});
// the 'abstract methods' to be implemented by child classes
/**
* Returns the current value of the bound target
*
* @function
* @name sap.ui.model.TreeBinding.prototype.getRootContexts
* @param {integer} iStartIndex the startIndex where to start the retrieval of contexts
* @param {integer} iLength determines how many contexts to retrieve beginning from the start index.
* @return {Array} the array of child contexts for the root node
*
* @public
*/
/**
* Returns the current value of the bound target
*
* @function
* @name sap.ui.model.TreeBinding.prototype.getNodeContexts
* @param {Object} oContext the context element of the node
* @param {integer} iStartIndex the startIndex where to start the retrieval of contexts
* @param {integer} iLength determines how many contexts to retrieve beginning from the start index.
* @return {Array} the array of child contexts for the given node
*
* @public
*/
/**
* Returns if the node has child nodes
*
* @function
* @name sap.ui.model.TreeBinding.prototype.hasChildren
* @param {Object} oContext the context element of the node
* @return {boolean} true if node has children
*
* @public
*/
/**
* Returns the number of child nodes of a specific context
*
* @param {Object} oContext the context element of the node
* @return {integer} the number of children
*
* @public
*/
TreeBinding.prototype.getChildCount = function(oContext) {
if (!oContext) {
return this.getRootContexts().length;
}
return this.getNodeContexts(oContext).length;
};
/**
* Filters the tree according to the filter definitions.
*
* @function
* @name sap.ui.model.TreeBinding.prototype.filter
* @param {sap.ui.model.Filter[]} aFilters Array of sap.ui.model.Filter objects
* @param {sap.ui.model.FilterType} sFilterType Type of the filter which should be adjusted, if it is not given, the standard behaviour applies
*
* @public
*/
/**
* Attach event-handler <code>fnFunction</code> to the '_filter' event of this <code>sap.ui.model.TreeBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event. It now contains a parameter (reason : "filter") when a filter event is fired.
*/
TreeBinding.prototype.attachFilter = function(fnFunction, oListener) {
this.attachEvent("_filter", fnFunction, oListener);
};
/**
* Detach event-handler <code>fnFunction</code> from the '_filter' event of this <code>sap.ui.model.TreeBinding</code>.<br/>
* @param {function} fnFunction The function to call, when the event occurs.
* @param {object} [oListener] object on which to call the given function.
* @protected
* @deprecated use the change event.
*/
TreeBinding.prototype.detachFilter = function(fnFunction, oListener) {
this.detachEvent("_filter", fnFunction, oListener);
};
/**
* Fire event _filter to attached listeners.
* @param {Map} [mArguments] the arguments to pass along with the event.
* @private
* @deprecated use the change event. It now contains a parameter (reason : "filter") when a filter event is fired.
*/
TreeBinding.prototype._fireFilter = function(mArguments) {
this.fireEvent("_filter", mArguments);
};
return TreeBinding;
}, /* bExport= */ true);
},
"sap/ui/model/Type.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the base implementation for all model implementations
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Object'],
function(jQuery, BaseObject) {
"use strict";
/**
* Constructor for a new Type.
*
* @class
* This is an abstract base class for type objects.
* @abstract
*
* @extends sap.ui.base.Object
*
* @author SAP SE
* @version 1.28.5
*
* @constructor
* @public
* @alias sap.ui.model.Type
*/
var Type = BaseObject.extend("sap.ui.model.Type", /** @lends sap.ui.model.Type.prototype */ {
constructor : function () {
BaseObject.apply(this, arguments);
this.sName = "Type";
},
metadata : {
"abstract" : true,
publicMethods : [
// methods
"getName"
]
}
});
/**
* Returns the name of this type.
*
* @return {String} the name of this type
* @public
*/
Type.prototype.getName = function() {
return this.sName;
};
return Type;
}, /* bExport= */ true);
},
"sap/ui/model/ValidateException.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides a filter for list bindings
sap.ui.define(['jquery.sap.global', 'sap/ui/base/Exception'],
function(jQuery, Exception) {
"use strict";
/**
* ValidateException class
*
* This exception is thrown, when a validation error occurs while checking the
* defined constraints for a type.
* @alias sap.ui.model.ValidateException
*/
var ValidateException = function(message, violatedConstraints) {
this.name = "ValidateException";
this.message = message;
this.violatedConstraints = violatedConstraints;
};
ValidateException.prototype = jQuery.sap.newObject(Exception.prototype);
return ValidateException;
}, /* bExport= */ true);
},
"sap/ui/model/message/MessageListBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the JSON model implementation of a list binding
sap.ui.define(['jquery.sap.global', 'sap/ui/model/ChangeReason', 'sap/ui/model/ClientListBinding'],
function(jQuery, ChangeReason, ClientListBinding) {
"use strict";
/**
*
* @class
* List binding implementation for Messages
*
* @param {sap.ui.model.message.MessageModel} oModel
* @param {string} sPath
* @param {sap.ui.model.Context} oContext
* @param {sap.ui.model.Sorter|sap.ui.model.Sorter[]} [aSorters] initial sort order (can be either a sorter or an array of sorters)
* @param {sap.ui.model.Filter|sap.ui.model.Filter[]} [aFilters] predefined filter/s (can be either a filter or an array of filters)
* @param {object} [mParameters]
* @alias sap.ui.model.message.MessageListBinding
* @extends sap.ui.model.ListBinding
*/
var MessageListBinding = ClientListBinding.extend("sap.ui.model.message.MessageListBinding");
/**
* Return contexts for the list or a specified subset of contexts
* @param {int} [iStartIndex=0] the startIndex where to start the retrieval of contexts
* @param {int} [iLength=length of the list] determines how many contexts to retrieve beginning from the start index.
* Default is the whole list length.
*
* @return {Array} the contexts array
* @protected
*/
MessageListBinding.prototype.getContexts = function(iStartIndex, iLength) {
this.iLastStartIndex = iStartIndex;
this.iLastLength = iLength;
if (!iStartIndex) {
iStartIndex = 0;
}
if (!iLength) {
iLength = Math.min(this.iLength, this.oModel.iSizeLimit);
}
var aContexts = this._getContexts(iStartIndex, iLength),
oContextData = {};
if (this.bUseExtendedChangeDetection) {
for (var i = 0; i < aContexts.length; i++) {
oContextData[aContexts[i].getPath()] = aContexts[i].getObject();
}
//Check diff
if (this.aLastContexts && iStartIndex < this.iLastEndIndex) {
var that = this;
var aDiff = jQuery.sap.arrayDiff(this.aLastContexts, aContexts, function(oOldContext, oNewContext) {
return jQuery.sap.equal(
oOldContext && that.oLastContextData && that.oLastContextData[oOldContext.getPath()],
oNewContext && oContextData && oContextData[oNewContext.getPath()]
);
});
aContexts.diff = aDiff;
}
this.iLastEndIndex = iStartIndex + iLength;
this.aLastContexts = aContexts.slice(0);
this.oLastContextData = jQuery.extend(true, {}, oContextData);
}
return aContexts;
};
/**
* Update the list, indices array and apply sorting and filtering
* @private
*/
MessageListBinding.prototype.update = function(){
var oList = this.oModel._getObject(this.sPath, this.oContext);
if (oList && jQuery.isArray(oList)) {
if (this.bUseExtendedChangeDetection) {
this.oList = jQuery.extend(true, [], oList);
} else {
this.oList = oList.slice(0);
}
this.updateIndices();
this.applyFilter();
this.applySort();
this.iLength = this._getLength();
} else {
this.oList = [];
this.aIndices = [];
this.iLength = 0;
}
};
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} bForceupdate
*
*/
MessageListBinding.prototype.checkUpdate = function(bForceupdate){
if (this.bSuspended && !this.bIgnoreSuspend) {
return;
}
if (!this.bUseExtendedChangeDetection) {
var oList = this.oModel._getObject(this.sPath, this.oContext);
if (!jQuery.sap.equal(this.oList, oList) || bForceupdate) {
this.update();
this._fireChange({reason: ChangeReason.Change});
}
} else {
var bChangeDetected = false;
var that = this;
//If the list has changed we need to update the indices first
var oList = this.oModel._getObject(this.sPath, this.oContext);
if (!jQuery.sap.equal(this.oList, oList)) {
this.update();
}
//Get contexts for visible area and compare with stored contexts
var aContexts = this._getContexts(this.iLastStartIndex, this.iLastLength);
if (this.aLastContexts) {
if (this.aLastContexts.length != aContexts.length) {
bChangeDetected = true;
} else {
jQuery.each(this.aLastContexts, function(iIndex, oContext) {
if (!jQuery.sap.equal(aContexts[iIndex].getObject(), that.oLastContextData[oContext.getPath()])) {
bChangeDetected = true;
return false;
}
});
}
} else {
bChangeDetected = true;
}
if (bChangeDetected || bForceupdate) {
this._fireChange({reason: ChangeReason.Change});
}
}
};
return MessageListBinding;
}, /* bExport= */ true);
},
"sap/ui/model/message/MessageModel.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/**
* Message DataBinding
*
* @namespace
* @name sap.ui.model.message
* @public
*/
// Provides the Message based model implementation
sap.ui.define(['jquery.sap.global', 'sap/ui/model/ClientModel', './MessageListBinding', './MessagePropertyBinding'],
function(jQuery, ClientModel, MessageListBinding, MessagePropertyBinding) {
"use strict";
/**
* Constructor for a new JSONModel.
*
* @class
* Model implementation for Messages
* *
* @extends sap.ui.model.Model
*
* @author SAP SE
* @version 1.28.5
*
* @param {sap.ui.core.message.MessageManager} oMessageManager The MessageManager instance
* @constructor
* @public
* @alias sap.ui.model.message.MessageModel
*/
var MessageModel = ClientModel.extend("sap.ui.model.message.MessageModel", /** @lends sap.ui.model.message.MessageModel.prototype */ {
constructor : function(oMessageManager) {
ClientModel.apply(this, arguments);
this.sDefaultBindingMode = sap.ui.model.BindingMode.OneWay;
this.mSupportedBindingModes = {
"OneWay" : true,
"TwoWay" : false,
"OneTime" : false
};
this.oMessageManager = oMessageManager;
}
});
/**
* Sets the message data to the model.
*
* @param {object} oData the data to set on the model
*
* @public
*/
MessageModel.prototype.setData = function(oData){
this.oData = oData;
this.checkUpdate();
};
MessageModel.prototype.fireMessageChange = function(mArguments) {
this.fireEvent("messageChange", mArguments);
return this;
};
/**
* @see sap.ui.model.Model.prototype.bindProperty
*
*/
MessageModel.prototype.bindProperty = function(sPath, oContext, mParameters) {
var oBinding = new MessagePropertyBinding(this, sPath, oContext, mParameters);
return oBinding;
};
/**
* @see sap.ui.model.Model.prototype.bindList
*
*/
MessageModel.prototype.bindList = function(sPath, oContext, aSorters, aFilters, mParameters) {
var oBinding = new MessageListBinding(this, sPath, oContext, aSorters, aFilters, mParameters);
return oBinding;
};
/**
* Sets a new value for the given property <code>sPropertyName</code> in the model.
* If the model value changed all interested parties are informed.
*
* @param {string} sPath path of the property to set
* @param {any} oValue value to set the property to
* @param {object} [oContext=null] the context which will be used to set the property
* @public
*/
MessageModel.prototype.setProperty = function(sPath, oValue, oContext) {
//not implemented: Only 'OneWay' binding mode supported
jQuery.sap.log.error(this + "not implemented: Only 'OneWay' binding mode supported");
};
/**
* Returns the value for the property with the given <code>sPropertyName</code>
*
* @param {string} sPath the path to the property
* @param {object} [oContext=null] the context which will be used to retrieve the property
* @type any
* @return the value of the property
* @public
*/
MessageModel.prototype.getProperty = function(sPath, oContext) {
return this._getObject(sPath, oContext);
};
/**
* @param {string} sPath
* @param {object} [oContext]
* @returns {any} the node of the specified path/context
*/
MessageModel.prototype._getObject = function (sPath, oContext) {
var oNode;
if (oContext instanceof sap.ui.model.Context) {
oNode = this._getObject(oContext.getPath());
}
if (!sPath) {
return oNode;
}
var aParts = sPath.split("/"),
iIndex = 0;
if (!aParts[0]) {
// absolute path starting with slash
oNode = this.oData;
iIndex++;
}
while (oNode && aParts[iIndex]) {
oNode = oNode[aParts[iIndex]];
iIndex++;
}
return oNode;
};
return MessageModel;
}, /* bExport= */ true);
},
"sap/ui/model/message/MessagePropertyBinding.js":function(){/*!
* SAP UI development toolkit for HTML5 (SAPUI5/OpenUI5)
* (c) Copyright 2009-2015 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides the JSON model implementation of a property binding
sap.ui.define(['jquery.sap.global', 'sap/ui/model/ClientPropertyBinding'],
function(jQuery, ClientPropertyBinding) {
"use strict";
/**
*
* @class
* Property binding implementation for Messages
*
* @param {sap.ui.model.message.MessageModel} oModel
* @param {string} sPath
* @param {sap.ui.model.Context} oContext
* @param {object} [mParameters]
* @alias sap.ui.model.message.MessagePropertyBinding
* @extends sap.ui.model.PropertyBinding
*/
var MessagePropertyBinding = ClientPropertyBinding.extend("sap.ui.model.message.MessagePropertyBinding");
/**
* @see sap.ui.model.PropertyBinding.prototype.setValue
*/
MessagePropertyBinding.prototype.setValue = function(oValue){
if (!jQuery.sap.equal(this.oValue, oValue)) {
// the binding value will be updated by the model. The model calls checkupdate on all bindings after updating its value.
this.oModel.setProperty(this.sPath, oValue, this.oContext);
}
};
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} bForceupdate
*
*/
MessagePropertyBinding.prototype.checkUpdate = function(bForceupdate){
var oValue = this._getValue();
if (!jQuery.sap.equal(oValue, this.oValue) || bForceupdate) {// optimize for not firing the events when unneeded
this.oValue = oValue;
this._fireChange({reason: sap.ui.model.ChangeReason.Change});
}
};
return MessagePropertyBinding;
}, /* bExport= */ true);
}
}});
jQuery.sap.require("sap.ui.core.Core");
// as this module contains the Core, we ensure that the Core has been booted
sap.ui.getCore().boot && sap.ui.getCore().boot();