// put this here so we could turn on/off the logging, it's soon enough after the creation of ST.ready if (!ST.isSandbox) { if (document.readyState !== 'complete') { ST.logger.debug('init.js, document.readyState='+document.readyState+', so wait for window load event to unblock ST.ready'); ST.ready.block(); var fn = function() { ST.logger.debug('init.js, document ready state change, readyState=' + document.readyState); if (document.readyState === 'complete') { ST.ready.unblock(); } }; if (document.attachEvent) { document.attachEvent('onreadystatechange', fn); } else { document.addEventListener('readystatechange', fn); } }} ST.typeOf = function (value) { var typeofTypes = { number: 1, string: 1, 'boolean': 1, 'undefined': 1 }, toStringTypes = { '[object Array]' : 'array', '[object Date]' : 'date', '[object Boolean]': 'boolean', '[object Number]' : 'number', '[object RegExp]' : 'regexp', '[object String]' : 'string' }; if (value === null) { return 'null'; } var type = typeof value, ret = type, typeToString, nonSpaceRe = /\S/; if (!typeofTypes[type]) { if (!(ret = toStringTypes[typeToString = ({}).toString.call(value)])) { if (type === 'function') { ret = type; } else if (type !== 'object') { ret = typeToString; } else if (value.nodeType === undefined) { ret = type; } else if (value.nodeType === 3) { ret = nonSpaceRe.test(value.nodeValue) ? 'textnode' : 'whitespace'; } else { ret = 'element'; } } } return ret;}; /** * Similar `setTimeout` but instead returns a function that cancels the timer. * * The timeout value (`millisec`) defaults to `ST.options.timeout` unless that value * is set to `0` in which case timeouts are disabled. In that case, `fn` will never be * called. * @param {Function} fn The function to call after `millisec` milliseconds. * @param {Object} [scope] The `this` pointer to use for calling `fn`. * @param {Number} [millisec] The delay in milliseconds. Defaults to `ST.options.timeout` * and is disabled if that value is `0`. * @return {Function} * @private * @member ST */ST.timeout = function (fn, scope, millisec) { var ms = ST.options.timeout; if (typeof scope === 'number') { millisec = scope; scope = null; } if (ms !== 0 && millisec != null) { // if ST.options.timeout is 0, ignore all timeouts even explicit ones ms = millisec; } return ms ? ST.doTimeout(fn, scope, ms) : ST.emptyFn;}; ST.doTimeout = function (fn, scope, ms) { var cancelFn = function () { if (cancelFn.timerId > 0) { ST.deferCancel(cancelFn.timerId); cancelFn.timerId = 0; } return null; }; cancelFn.timerId = ST.defer(fn, scope, ms); cancelFn.timeout = ms; return cancelFn;}; /** * @member ST * Pretty print * @param value * @private */ST.prettyPrint = function(value) { var formattedValue = value, className, superclass, id, type; if (value) { className = value.$className; if (className !== undefined) { // support for pretty printing instances of Ext classes if (!className) { // support for anonymous classes - Ext.define(null, ...) // loop up the inheritance chain to find nearest non-anonymous ancestor superclass = value.superclass; while (superclass && !superclass.$className) { superclass = superclass.superclass; } if (superclass) { className = superclass.$className; } } id = value.id || (value.getId && value.getId()); formattedValue = className + (id ? ('#' + id) : ''); } else if (value instanceof Array) { formattedValue = 'Array'; } else { type = typeof value; if (type === 'string') { formattedValue = '"' + value + '"'; } else if (type === 'boolean' || type === 'number') { formattedValue = value; } else if (value.tagName) { id = value.id; formattedValue = '<' + value.tagName.toLowerCase() + (id ? ('#' + id) : '') + '>'; } else if (type === 'function') { formattedValue = 'Function'; } else { formattedValue = 'Object'; } } } return formattedValue;}; ST.globRegex = function (glob, opts) { /* https://github.com/fitzgen/glob-to-regexp Copyright (c) 2013, Nick Fitzgerald All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ if (glob == null) { return null; } var str = String(glob), // The regexp we are building, as a string. reStr = "", // Whether we are matching so called "extended" globs (like bash) and should // support single character matching, matching ranges of characters, group // matching, etc. extended = opts ? !!opts.extended : false, // If we are doing extended matching, this boolean is true when we are inside // a group (eg {*.html,*.js}), and false otherwise. inGroup = false, // RegExp flags (eg "i" ) to pass in to RegExp constructor. flags = opts && typeof( opts.flags ) === "string" ? opts.flags : "", c, i, len; for (i = 0, len = str.length; i < len; i++) { c = str[i]; switch (c) { case "\\": case "/": case "$": case "^": case "+": case ".": case "(": case ")": case "=": case "!": case "|": reStr += "\\" + c; break; case "?": if (extended) { reStr += "."; break; } /* fallthrough */ case "[": case "]": if (extended) { reStr += c; break; } /* fallthrough */ case "{": if (extended) { inGroup = true; reStr += "("; break; } /* fallthrough */ case "}": if (extended) { inGroup = false; reStr += ")"; break; } /* fallthrough */ case ",": if (inGroup) { reStr += "|"; break; } reStr += "\\" + c; break; case "*": reStr += ".*"; break; default: reStr += c; } } // When regexp 'g' flag is specified don't // constrain the regular expression with ^ & $ if (!flags || !~flags.indexOf('g')) { reStr = "^" + reStr + "$"; } return new RegExp(reStr, flags);}; ST.initGlobals = function () { var globals = ST.options.globals, covRe = '/^__cov_/', prop; // Any properties already in the window object are ok for (prop in window) { globals[prop] = true; } // Old Firefox needs these globals.getInterface = globals.loadFirebugConsole = globals._createFirebugConsole = globals.netscape = globals.XPCSafeJSObjectWrapper = globals.XPCNativeWrapper = globals.Components = globals._firebug = // IE10+ F12 dev tools adds these properties when opened. globals.__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE = globals.__BROWSERTOOLS_CONSOLE_BREAKMODE_FUNC = globals.__BROWSERTOOLS_CONSOLE_SAFEFUNC = // in IE8 jasmine's overrides of setTimeout/setInterval make them iterable globals.setTimeout = globals.setInterval = globals.clearTimeout = globals.clearInterval = // In Ext JS 4 Ext.get(window) adds an id property globals.id = // Temporary namespace used by serve/contex/WebDriver to store exec/ready/call functions // for deferred debugging. globals.$ST = // new standard globals, found in chrome globals.customElements = globals.chrome = globals.external = true; ST.options.globalPatterns[covRe] = ST.globRegex(covRe); ST.options.globalsInited = true;}; (function () { var idRe = /^[a-z$_][a-z0-9$_.]*$/i; /** * Adds one or more allowable global variable names. * Variable names can be simple names or a regex. * @param {String/String[]} add * @member ST */ ST.addGlobals = function () { var globals = ST.options.globals, args = arguments, i = args.length, s; while (i-- > 0) { if (!(s = args[i])) { continue; } if (typeof s === 'string') { ST.options.contextGlobals = ST.options.contextGlobals || []; ST.options.contextGlobals.push(s); if (idRe.test(s)) { globals[s] = true; // simple names can be in a map } else { ST.options.globalPatterns[s] = ST.globRegex(s); } } else { ST.addGlobals.apply(ST,s); // not a String so must be a String[] } } }; ST.checkGlobalLeaks = function () { var allowedGlobals = ST.options.globals, globalPatterns = ST.options.globalPatterns, result = { results: [], addedGlobals: [] }, i, ok, property, value; for (property in window) { if (allowedGlobals[property]) { // Reading some properties from window can trigger warnings (such as // webkitStorageInfo), so skip early. continue; } for (i in globalPatterns) { ok = globalPatterns[i].test(property); if (ok) break; } if (ok) { continue; } try { // IE throws error when trying to access window.localStorage value = window[property]; } catch (e) { continue; } if (value !== undefined && (!value || // make sure we don't try to do a property lookup on a null value // old browsers (IE6 and opera 11) add element IDs as enumerable properties // of the window object, so make sure the global var is not a HTMLElement value.nodeType !== 1 && // make sure it isn't a reference to a window object. This happens in // some browsers (e.g. IE6) when the document contains iframes. The // frames' window objects are referenced by id in the parent window object. !(value.location && value.document))) { // add the bad global to allowed globals so that it only fails this one spec result.addedGlobals.push(property); result.results.push({ passed: false, message: 'Bad global variable: ' + property + ' = ' + ST.prettyPrint(value) }); } } return result; }; ST.setupOptions = function (testOptions) { var options = ST.options, globals = options.globals, add; if (testOptions) { ST.apply(options, testOptions); add = testOptions.globals; if (add !== undefined) { options.globals = globals; // put back the original globals ST.addGlobals(add); } } ST.ready.on(function () { ST.initGlobals(); }); };})(); ST.decodeRegex = function (value) { var regex = value, isRegex = regex && typeof regex === 'object' && regex.isRegex; if (isRegex) { regex = new RegExp(regex.source, regex.flags); } return regex;}; ST.encodeRegex = function (value) { // a regex object won't survive serialization, so deconstruct it so // we can put it back together on the other side if (value instanceof RegExp) { var testFlags = { global: 'g', ignoreCase: 'i', multiline: 'm', sticky: 'y', unicode: 'u' }, flags = '', key; // loop over detected flags and stringify them // we can't use regex.flags since it's not standardized... for (key in testFlags) { if (value[key]) { flags += testFlags[key]; } } value = { isRegex: true, source: value.source, flags: flags }; } return value;}; ST.getScrollbarSize = function (force) { var scrollbarSize = ST._scrollbarSize; if (force || !scrollbarSize) { var db = document.body, div = document.createElement('div'); div.style.width = div.style.height = '100px'; div.style.overflow = 'scroll'; div.style.position = 'absolute'; db.appendChild(div); // now we can measure the div... // at least in iE9 the div is not 100px - the scrollbar size is removed! ST._scrollbarSize = scrollbarSize = { width: div.offsetWidth - div.clientWidth, height: div.offsetHeight - div.clientHeight }; db.removeChild(div); } return scrollbarSize;};