diff --git a/dev/Common/Booter.jsx b/dev/Common/Booter.jsx new file mode 100644 index 000000000..c821cbdb2 --- /dev/null +++ b/dev/Common/Booter.jsx @@ -0,0 +1,163 @@ + +import window from 'window'; +import $ from '$'; + +import rainLoopStorage from 'Storage/RainLoop'; + +window.__rlah = () => { + return rainLoopStorage ? rainLoopStorage.getHash() : null; +}; + +window.__rlah_set = () => { + if (rainLoopStorage) + { + rainLoopStorage.setHash(); + } +}; + +window.__rlah_clear = () => { + if (rainLoopStorage) + { + rainLoopStorage.clearHash(); + } +}; + +window.__includeScr = (src) => { + window.document.write(unescape('%3Csc' + 'ript data-cfasync="false" type="text/jav' + 'ascr' + 'ipt" sr' + 'c="' + src + '"%3E%3C/' + 'scr' + 'ipt%3E')); +}; + +window.__includeStyle = (styles) => { + window.document.write(unescape('%3Csty' + 'le%3E' + styles + '"%3E%3C/' + 'sty' + 'le%3E')); +}; + +window.__showError = (additionalError) => { + + const + oR = window.document.getElementById('rl-loading'), + oL = window.document.getElementById('rl-loading-error'), + oLA = window.document.getElementById('rl-loading-error-additional') + ; + + if (oR) + { + oR.style.display = 'none'; + } + + if (oL) + { + oL.style.display = 'block'; + } + + if (oLA && additionalError) + { + oLA.style.display = 'block'; + oLA.innerHTML = additionalError; + } + + if (window.rainloopProgressJs) + { + window.rainloopProgressJs.set(100); + } +}; + +window.__showDescription = (description) => { + + const + oE = window.document.getElementById('rl-loading'), + oElDesc = window.document.getElementById('rl-loading-desc') + ; + + if (oElDesc && description) + { + oElDesc.innerHTML = description; + } + + if (oE && oE.style) + { + oE.style.opacity = 0; + window.setTimeout(() => { + oE.style.opacity = 1; + }, 300); + } +}; + +window.__runBoot = (withError, additionalError) => { + if (window.__APP_BOOT && !withError) { + window.__APP_BOOT(function (bV) { + if (!bV) { + window.__showError(additionalError); + } + }); + } else { + window.__showError(additionalError); + } +}; + +window.__runApp = (BaseAppLibsScriptLink, BaseAppMainScriptLink, BaseAppEditorScriptLink) => { + + const appData = window.rainloopAppData; + + if (window.$LAB && window.progressJs && appData && appData.TemplatesLink && appData.LangLink) + { + const p = window.progressJs(); + window.rainloopProgressJs = p; + + p.setOptions({theme: 'rainloop'}); + p.start().set(5); + + window.$LAB + .script(function () { + return [ + {src: BaseAppLibsScriptLink, type: 'text/javascript', charset: 'utf-8'} + ]; + }) + .wait(function () { + + p.set(20); + + if (appData.IncludeBackground && $) { + $('#rl-bg').attr('style', 'background-image: none !important;') + .backstretch(appData.IncludeBackground.replace('{{USER}}', + (window.__rlah ? window.__rlah() || '0' : '0')), {fade: 100, centeredX: true, centeredY: true}) + .removeAttr('style') + ; + } + }) + .script(function () { + return [ + {src: appData.TemplatesLink, type: 'text/javascript', charset: 'utf-8'}, + {src: appData.LangLink, type: 'text/javascript', charset: 'utf-8'} + ]; + }) + .wait(function () { + p.set(30); + }) + .script(function () { + return {src: BaseAppMainScriptLink, type: 'text/javascript', charset: 'utf-8'}; + }) + .wait(function () { + p.set(50); + }) + .script(function () { + return appData.PluginsLink ? {src: appData.PluginsLink, type: 'text/javascript', charset: 'utf-8'} : null; + }) + .wait(function () { + p.set(70); + window.__runBoot(false); + }) + .script(function () { + return {src: BaseAppEditorScriptLink, type: 'text/javascript', charset: 'utf-8'}; + }) + .wait(function () { + if (window.CKEDITOR && window.__initEditor) { + window.__initEditor(); + window.__initEditor = null; + } + }) + ; + } + else + { + window.__runBoot(true); + } +}; diff --git a/dev/Storage/RainLoop.jsx b/dev/Storage/RainLoop.jsx new file mode 100644 index 000000000..0ebc7ffe1 --- /dev/null +++ b/dev/Storage/RainLoop.jsx @@ -0,0 +1,62 @@ + +import window from 'window'; + +const STORAGE_KEY = '__rlA'; + +class RainLoopStorage +{ + s = null; + t = null; + + constructor() + { + this.s = window.sessionStorage || null; + this.t = window.top || window; + } + + getHash() { + let result = null; + if (this.s) + { + result = this.s.getItem(STORAGE_KEY) || null; + } + else if (this.t && JSON) + { + const data = this.t.name && '{' === this.t.name.toString().substr(0, 1) ? JSON.parse(this.t.name.toString()) : null; + result = data ? (data[STORAGE_KEY] || null) : null; + } + + return result; + } + + setHash() { + const + key = 'AuthAccountHash', + appData = window.rainloopAppData + ; + if (this.s) + { + this.s.setItem(STORAGE_KEY, appData && appData[key] ? appData[key] : ''); + } + else if (this.t && JSON) + { + let data = {}; + data[STORAGE_KEY] = appData && appData[key] ? appData[key] : ''; + + this.t.name = JSON.stringify(data); + } + } + + clearHash() { + if (this.s) + { + this.s.setItem(STORAGE_KEY, ''); + } + else if (this.t) + { + this.t.name = ''; + } + } +} + +module.exports = new RainLoopStorage(); diff --git a/dev/Styles/Main.less b/dev/Styles/Main.less index 1146ebecb..684fb162b 100644 --- a/dev/Styles/Main.less +++ b/dev/Styles/Main.less @@ -51,7 +51,7 @@ select:focus { outline: none; } -html.mobile * { +html.mobile *, html.rl-mobile * { -webkit-tap-highlight-color: rgba(0,0,0,0); } diff --git a/dev/boot.jsx b/dev/boot.jsx new file mode 100644 index 000000000..e15efc72e --- /dev/null +++ b/dev/boot.jsx @@ -0,0 +1,12 @@ + +import window from 'window'; + +require('json2/json2.js'); +require('modernizr/modernizr-custom.js'); +require('Common/Booter.jsx'); + +import {$LAB} from 'labjs/LAB.src.js'; +import {progressJs} from 'progress.js/src/progress.js'; + +window.$LAB = $LAB; +window.progressJs = progressJs; diff --git a/gulpfile.js b/gulpfile.js index e278bd1c5..922754975 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -141,16 +141,6 @@ cfg.paths.css = { }; cfg.paths.js = { - boot: { - name: 'boot.js', - src: [ - 'vendors/json2.min.js', - 'vendors/labjs/LAB.min.js', - 'vendors/modernizr.js', - 'vendors/progress.js/minified/progress.min.js', - 'vendors/rl/rl-1.5.min.js' - ] - }, openpgp: { name: 'openpgp.min.js', src: [ @@ -293,13 +283,6 @@ gulp.task('css:main:min', ['css:main'], function() { }); // JS -gulp.task('js:boot', function() { - return gulp.src(cfg.paths.js.boot.src) - .pipe(concat(cfg.paths.js.boot.name)) - .pipe(eol('\n', true)) - .pipe(gulp.dest(cfg.paths.staticMinJS)); -}); - gulp.task('js:encrypt', function() { return gulp.src(cfg.paths.js.encrypt.src) .pipe(concat(cfg.paths.js.encrypt.name)) @@ -705,7 +688,7 @@ gulp.task('rainloop:owncloud:shortname', ['rainloop:owncloud:md5'], function(cal // MAIN gulp.task('js:pgp', ['js:openpgp', 'js:openpgpworker']); -gulp.task('default', ['js:libs', 'js:boot', 'js:pgp', 'js:min', 'css:main:min', 'ckeditor', 'fontastic']); +gulp.task('default', ['js:libs', 'js:pgp', 'js:min', 'css:main:min', 'ckeditor', 'fontastic']); gulp.task('fast-', ['js:app', 'js:admin', 'js:chunks', 'css:main']); gulp.task('fast', ['package:community-on', 'fast-']); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php index ee66948a8..c067e9545 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php @@ -194,9 +194,14 @@ class Service } $aTemplateParameters = $this->indexTemplateParameters($bAdmin, $bMobile, $bMobileDevice); + if (!empty($aTemplateParameters['{{BaseApplicationConfigurationJson}}'])) + { + $this->oActions->Logger()->Write($aTemplateParameters['{{BaseApplicationConfigurationJson}}'], + \MailSo\Log\Enumerations\Type::INFO, 'APP'); + } $sCacheFileName = ''; - if ($this->oActions->Config()->Get('labs', 'cache_system_data', true)) + if ($this->oActions->Config()->Get('labs', 'cache_system_data', true) && !empty($aTemplateParameters['{{BaseHash}}'])) { $sCacheFileName = 'TMPL:'.$aTemplateParameters['{{BaseHash}}']; $sResult = $this->oActions->Cacher()->Get($sCacheFileName); @@ -327,6 +332,18 @@ class Service ) : array()); } + /** + * @param string $sPath + * + * @return string + */ + private function staticPath($sPath) + { + $sStaticSuffix = $this->oActions->IsOpen() ? '?vo' : '?vs'; + + return \RainLoop\Utils::WebStaticPath().$sPath.$sStaticSuffix; + } + /** * @param bool $bAdmin = false * @param bool $bMobile = false @@ -346,21 +363,18 @@ class Service $sFaviconUrl = (string) $this->oActions->Config()->Get('webmail', 'favicon_url', ''); - $sStaticPrefix = \RainLoop\Utils::WebStaticPath(); - $aData = array( 'Language' => $sLanguage, 'Theme' => $sTheme, - 'FaviconPngLink' => $sFaviconUrl ? $sFaviconUrl : $sStaticPrefix.'favicon.png', - 'AppleTouchLink' => $sFaviconUrl ? '' : $sStaticPrefix.'apple-touch-icon.png', - 'AppCssLink' => $sStaticPrefix.'css/app'.($bAppCssDebug ? '' : '.min').'.css', - 'BootJsLink' => $sStaticPrefix.'js/min/boot.js', - 'ComponentsJsLink' => $sStaticPrefix.'js/'.($bAppJsDebug ? '' : 'min/').'components.js', - 'LibJsLink' => $sStaticPrefix.'js/min/libs.js', - 'EditorJsLink' => $sStaticPrefix.'ckeditor/ckeditor.js', - 'OpenPgpJsLink' => $sStaticPrefix.'js/min/openpgp.min.js', - 'AppJsCommonLink' => $sStaticPrefix.'js/'.($bAppJsDebug ? '' : 'min/').'common.js', - 'AppJsLink' => $sStaticPrefix.'js/'.($bAppJsDebug ? '' : 'min/').($bAdmin ? 'admin' : 'app').'.js' + 'FaviconPngLink' => $sFaviconUrl ? $sFaviconUrl : $this->staticPath('apple-touch-icon.png'), + 'AppleTouchLink' => $sFaviconUrl ? '' : $this->staticPath('apple-touch-icon.png'), + 'AppCssLink' => $this->staticPath('css/app'.($bAppCssDebug ? '' : '.min').'.css'), + 'BootJsLink' => $this->staticPath('js/'.($bAppJsDebug ? '' : 'min/').'boot.js'), + 'LibJsLink' => $this->staticPath('js/min/libs.js'), + 'EditorJsLink' => $this->staticPath('ckeditor/ckeditor.js'), + 'OpenPgpJsLink' => $this->staticPath('js/min/openpgp.min.js'), + 'AppJsCommonLink' => $this->staticPath('js/'.($bAppJsDebug ? '' : 'min/').'common.js'), + 'AppJsLink' => $this->staticPath('js/'.($bAppJsDebug ? '' : 'min/').($bAdmin ? 'admin' : 'app').'.js') ); $aTemplateParameters = array( @@ -370,7 +384,6 @@ class Service '{{BaseAppAppleTouchFile}}' => $aData['AppleTouchLink'], '{{BaseAppMainCssLink}}' => $aData['AppCssLink'], '{{BaseAppBootScriptLink}}' => $aData['BootJsLink'], - '{{BaseAppComponentsScriptLink}}' => $aData['ComponentsJsLink'], '{{BaseAppLibsScriptLink}}' => $aData['LibJsLink'], '{{BaseAppEditorScriptLink}}' => $aData['EditorJsLink'], '{{BaseAppOpenPgpScriptLink}}' => $aData['OpenPgpJsLink'], @@ -388,7 +401,8 @@ class Service $bAdmin ? '1' : '0', \md5($this->oActions->Config()->Get('cache', 'index', '')), $this->oActions->Plugins()->Hash(), - \RainLoop\Utils::WebVersionPath(), APP_VERSION + \RainLoop\Utils::WebVersionPath(), + APP_VERSION, )). \implode('~', $aTemplateParameters) ); diff --git a/rainloop/v/0.0.0/app/templates/Index.html b/rainloop/v/0.0.0/app/templates/Index.html index 743ee8cf6..cf8b65624 100644 --- a/rainloop/v/0.0.0/app/templates/Index.html +++ b/rainloop/v/0.0.0/app/templates/Index.html @@ -69,89 +69,7 @@
- - + + diff --git a/vendors/json2.min.js b/vendors/json2.min.js deleted file mode 100644 index aca6ca901..000000000 --- a/vendors/json2.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! See http://www.JSON.org/js.html */ -var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c= '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 = '', + uffff; + +// When parsing for string values, we must look for " and \ characters. + + if (ch === '"') { + while (next()) { + if (ch === '"') { + 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"); + }, + + 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) { + key = string(); + 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 JSON 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 '"': + return string(); + case '-': + return number(); + default: + return ch >= '0' && ch <= '9' + ? number() + : word(); + } + }; + +// Return the json_parse function. It will have access to all of the above +// functions and variables. + + return function (source, reviver) { + var result; + + text = source; + at = 0; + ch = ' '; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + +// If there is a reviver function, we recursively walk the new structure, +// passing each name/value pair to the reviver function for possible +// transformation, starting with a temporary root object that holds the result +// in an empty key. If there is not a reviver function, we simply return the +// result. + + return typeof reviver === 'function' + ? (function walk(holder, key) { + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + }({'': result}, '')) + : result; + }; +}()); diff --git a/vendors/json2/json_parse_state.js b/vendors/json2/json_parse_state.js new file mode 100644 index 000000000..beb0f97c8 --- /dev/null +++ b/vendors/json2/json_parse_state.js @@ -0,0 +1,403 @@ +/* + json_parse_state.js + 2015-05-02 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + This file creates a json_parse function. + + json_parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = json_parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + This is a reference implementation. You are free to copy, modify, or + redistribute. + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. +*/ + +/*jslint for */ + +/*property + acomma, avalue, b, call, colon, container, exec, f, false, firstavalue, + firstokey, fromCharCode, go, hasOwnProperty, key, length, n, null, ocomma, + okey, ovalue, pop, prototype, push, r, replace, slice, state, t, test, + true +*/ + +var json_parse = (function () { + "use strict"; + +// This function creates a JSON parse function that uses a state machine rather +// than the dangerous eval function to parse a JSON text. + + var state, // The state of the parser, one of + // 'go' The starting state + // 'ok' The final, accepting state + // 'firstokey' Ready for the first key of the object or + // the closing of an empty object + // 'okey' Ready for the next key of the object + // 'colon' Ready for the colon + // 'ovalue' Ready for the value half of a key/value pair + // 'ocomma' Ready for a comma or closing } + // 'firstavalue' Ready for the first value of an array or + // an empty array + // 'avalue' Ready for the next value of an array + // 'acomma' Ready for a comma or closing ] + stack, // The stack, for controlling nesting. + container, // The current container object or array + key, // The current key + value, // The current value + escapes = { // Escapement translation table + '\\': '\\', + '"': '"', + '/': '/', + 't': '\t', + 'n': '\n', + 'r': '\r', + 'f': '\f', + 'b': '\b' + }, + string = { // The actions for string tokens + go: function () { + state = 'ok'; + }, + firstokey: function () { + key = value; + state = 'colon'; + }, + okey: function () { + key = value; + state = 'colon'; + }, + ovalue: function () { + state = 'ocomma'; + }, + firstavalue: function () { + state = 'acomma'; + }, + avalue: function () { + state = 'acomma'; + } + }, + number = { // The actions for number tokens + go: function () { + state = 'ok'; + }, + ovalue: function () { + state = 'ocomma'; + }, + firstavalue: function () { + state = 'acomma'; + }, + avalue: function () { + state = 'acomma'; + } + }, + action = { + +// The action table describes the behavior of the machine. It contains an +// object for each token. Each object contains a method that is called when +// a token is matched in a state. An object will lack a method for illegal +// states. + + '{': { + go: function () { + stack.push({state: 'ok'}); + container = {}; + state = 'firstokey'; + }, + ovalue: function () { + stack.push({container: container, state: 'ocomma', key: key}); + container = {}; + state = 'firstokey'; + }, + firstavalue: function () { + stack.push({container: container, state: 'acomma'}); + container = {}; + state = 'firstokey'; + }, + avalue: function () { + stack.push({container: container, state: 'acomma'}); + container = {}; + state = 'firstokey'; + } + }, + '}': { + firstokey: function () { + var pop = stack.pop(); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + }, + ocomma: function () { + var pop = stack.pop(); + container[key] = value; + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + } + }, + '[': { + go: function () { + stack.push({state: 'ok'}); + container = []; + state = 'firstavalue'; + }, + ovalue: function () { + stack.push({container: container, state: 'ocomma', key: key}); + container = []; + state = 'firstavalue'; + }, + firstavalue: function () { + stack.push({container: container, state: 'acomma'}); + container = []; + state = 'firstavalue'; + }, + avalue: function () { + stack.push({container: container, state: 'acomma'}); + container = []; + state = 'firstavalue'; + } + }, + ']': { + firstavalue: function () { + var pop = stack.pop(); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + }, + acomma: function () { + var pop = stack.pop(); + container.push(value); + value = container; + container = pop.container; + key = pop.key; + state = pop.state; + } + }, + ':': { + colon: function () { + if (Object.hasOwnProperty.call(container, key)) { + throw new SyntaxError('Duplicate key "' + key + '"'); + } + state = 'ovalue'; + } + }, + ',': { + ocomma: function () { + container[key] = value; + state = 'okey'; + }, + acomma: function () { + container.push(value); + state = 'avalue'; + } + }, + 'true': { + go: function () { + value = true; + state = 'ok'; + }, + ovalue: function () { + value = true; + state = 'ocomma'; + }, + firstavalue: function () { + value = true; + state = 'acomma'; + }, + avalue: function () { + value = true; + state = 'acomma'; + } + }, + 'false': { + go: function () { + value = false; + state = 'ok'; + }, + ovalue: function () { + value = false; + state = 'ocomma'; + }, + firstavalue: function () { + value = false; + state = 'acomma'; + }, + avalue: function () { + value = false; + state = 'acomma'; + } + }, + 'null': { + go: function () { + value = null; + state = 'ok'; + }, + ovalue: function () { + value = null; + state = 'ocomma'; + }, + firstavalue: function () { + value = null; + state = 'acomma'; + }, + avalue: function () { + value = null; + state = 'acomma'; + } + } + }; + + function debackslashify(text) { + +// Remove and replace any backslash escapement. + + return text.replace(/\\(?:u(.{4})|([^u]))/g, function (ignore, b, c) { + return b + ? String.fromCharCode(parseInt(b, 16)) + : escapes[c]; + }); + } + + return function (source, reviver) { + +// A regular expression is used to extract tokens from the JSON text. +// The extraction process is cautious. + + var result, + tx = /^[\u0020\t\n\r]*(?:([,:\[\]{}]|true|false|null)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|"((?:[^\r\n\t\\\"]|\\(?:["\\\/trnfb]|u[0-9a-fA-F]{4}))*)")/; + +// Set the starting state. + + state = 'go'; + +// The stack records the container, key, and state for each object or array +// that contains another object or array while processing nested structures. + + stack = []; + +// If any error occurs, we will catch it and ultimately throw a syntax error. + + try { + +// For each token... + + while (true) { + result = tx.exec(source); + if (!result) { + break; + } + +// result is the result array from matching the tokenizing regular expression. +// result[0] contains everything that matched, including any initial whitespace. +// result[1] contains any punctuation that was matched, or true, false, or null. +// result[2] contains a matched number, still in string form. +// result[3] contains a matched string, without quotes but with escapement. + + if (result[1]) { + +// Token: Execute the action for this state and token. + + action[result[1]][state](); + + } else if (result[2]) { + +// Number token: Convert the number string into a number value and execute +// the action for this state and number. + + value = +result[2]; + number[state](); + } else { + +// String token: Replace the escapement sequences and execute the action for +// this state and string. + + value = debackslashify(result[3]); + string[state](); + } + +// Remove the token from the string. The loop will continue as long as there +// are tokens. This is a slow process, but it allows the use of ^ matching, +// which assures that no illegal tokens slip through. + + source = source.slice(result[0].length); + } + +// If we find a state/token combination that is illegal, then the action will +// cause an error. We handle the error by simply changing the state. + + } catch (e) { + state = e; + } + +// The parsing is finished. If we are not in the final 'ok' state, or if the +// remaining source contains anything except whitespace, then we did not have +//a well-formed JSON text. + + if (state !== 'ok' || (/[^\u0020\t\n\r]/.test(source))) { + throw state instanceof SyntaxError + ? state + : new SyntaxError('JSON'); + } + +// If there is a reviver function, we recursively walk the new structure, +// passing each name/value pair to the reviver function for possible +// transformation, starting with a temporary root object that holds the current +// value in an empty key. If there is not a reviver function, we simply return +// that value. + + return typeof reviver === 'function' + ? (function walk(holder, key) { + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + }({'': value}, '')) + : value; + }; +}()); diff --git a/vendors/modernizr.js b/vendors/modernizr.js deleted file mode 100644 index 655f2d5cd..000000000 --- a/vendors/modernizr.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Modernizr 2.6.2 (Custom Build) | MIT & BSD - * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-svg-touch-shiv-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-fullscreen_api - */ -;window.Modernizr=function(a,b,c){function C(a){j.cssText=a}function D(a,b){return C(n.join(a+";")+(b||""))}function E(a,b){return typeof a===b}function F(a,b){return!!~(""+a).indexOf(b)}function G(a,b){for(var d in a){var e=a[d];if(!F(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function H(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:E(f,"function")?f.bind(d||b):f}return!1}function I(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return E(b,"string")||E(b,"undefined")?G(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),H(e,b,c))}function J(){e.input=function(c){for(var d=0,e=c.length;d',a,""].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},z=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=E(e[d],"function"),E(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),A={}.hasOwnProperty,B;!E(A,"undefined")&&!E(A.call,"undefined")?B=function(a,b){return A.call(a,b)}:B=function(a,b){return b in a&&E(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return I("flexWrap")},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!I("indexedDB",a)},s.hashchange=function(){return z("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return C("background-color:rgba(150,255,150,.5)"),F(j.backgroundColor,"rgba")},s.hsla=function(){return C("background-color:hsla(120,40%,100%,.5)"),F(j.backgroundColor,"rgba")||F(j.backgroundColor,"hsla")},s.multiplebgs=function(){return C("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return I("backgroundSize")},s.borderimage=function(){return I("borderImage")},s.borderradius=function(){return I("borderRadius")},s.boxshadow=function(){return I("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return D("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return I("animationName")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return C((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),F(j.backgroundImage,"gradient")},s.cssreflections=function(){return I("boxReflect")},s.csstransforms=function(){return!!I("transform")},s.csstransforms3d=function(){var a=!!I("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return I("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect};for(var K in s)B(s,K)&&(x=K.toLowerCase(),e[x]=s[K](),v.push((e[x]?"":"no-")+x));return e.input||J(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)B(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},C(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.hasEvent=z,e.testProp=function(a){return G([a])},e.testAllProps=I,e.testStyles=y,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),Modernizr.addTest("fullscreen",function(){for(var a=0;a` element. This + * information allows you to progressively enhance your pages with a granular level + * of control over the experience. +*/ + +;(function(window, document, undefined){ + var classes = []; + + + var tests = []; + + + /** + * + * ModernizrProto is the constructor for Modernizr + * + * @class + * @access public + */ + + var ModernizrProto = { + // The current version, dummy + _version: '3.3.1', + + // Any settings that don't work as separate modules + // can go in here as configuration. + _config: { + 'classPrefix': '', + 'enableClasses': true, + 'enableJSClass': true, + 'usePrefixes': true + }, + + // Queue of tests + _q: [], + + // Stub these for people who are listening + on: function(test, cb) { + // I don't really think people should do this, but we can + // safe guard it a bit. + // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. + // This is in case people listen to synchronous tests. I would leave it out, + // but the code to *disallow* sync tests in the real version of this + // function is actually larger than this. + var self = this; + setTimeout(function() { + cb(self[test]); + }, 0); + }, + + addTest: function(name, fn, options) { + tests.push({name: name, fn: fn, options: options}); + }, + + addAsyncTest: function(fn) { + tests.push({name: null, fn: fn}); + } + }; + + + + // Fake some of Object.create so we can force non test results to be non "own" properties. + var Modernizr = function() {}; + Modernizr.prototype = ModernizrProto; + + // Leak modernizr globally when you `require` it rather than force it here. + // Overwrite name so constructor name is nicer :D + Modernizr = new Modernizr(); + + + + /** + * is returns a boolean if the typeof an obj is exactly type. + * + * @access private + * @function is + * @param {*} obj - A thing we want to check the type of + * @param {string} type - A string to compare the typeof against + * @returns {boolean} + */ + + function is(obj, type) { + return typeof obj === type; + } + ; + + /** + * Run through all tests and detect their support in the current UA. + * + * @access private + */ + + function testRunner() { + var featureNames; + var feature; + var aliasIdx; + var result; + var nameIdx; + var featureName; + var featureNameSplit; + + for (var featureIdx in tests) { + if (tests.hasOwnProperty(featureIdx)) { + featureNames = []; + feature = tests[featureIdx]; + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + // + // If there is no name, it's an 'async' test that is run, + // but not directly added to the object. That should + // be done with a post-run addTest call. + if (feature.name) { + featureNames.push(feature.name.toLowerCase()); + + if (feature.options && feature.options.aliases && feature.options.aliases.length) { + // Add all the aliases into the names list + for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { + featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); + } + } + } + + // Run the test, or use the raw value if it's not a function + result = is(feature.fn, 'function') ? feature.fn() : feature.fn; + + + // Set each of the names on the Modernizr object + for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { + featureName = featureNames[nameIdx]; + // Support dot properties as sub tests. We don't do checking to make sure + // that the implied parent tests have been added. You must call them in + // order (either in the test, or make the parent test a dependency). + // + // Cap it to TWO to make the logic simple and because who needs that kind of subtesting + // hashtag famous last words + featureNameSplit = featureName.split('.'); + + if (featureNameSplit.length === 1) { + Modernizr[featureNameSplit[0]] = result; + } else { + // cast to a Boolean, if not one already + /* jshint -W053 */ + if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { + Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); + } + + Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; + } + + classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); + } + } + } + } + ; + + /** + * docElement is a convenience wrapper to grab the root element of the document + * + * @access private + * @returns {HTMLElement|SVGElement} The root element of the document + */ + + var docElement = document.documentElement; + + + /** + * A convenience helper to check if the document we are running in is an SVG document + * + * @access private + * @returns {boolean} + */ + + var isSVG = docElement.nodeName.toLowerCase() === 'svg'; + + + /** + * setClasses takes an array of class names and adds them to the root element + * + * @access private + * @function setClasses + * @param {string[]} classes - Array of class names + */ + + // Pass in an and array of class names, e.g.: + // ['no-webp', 'borderradius', ...] + function setClasses(classes) { + var className = docElement.className; + var classPrefix = Modernizr._config.classPrefix || ''; + + if (isSVG) { + className = className.baseVal; + } + + // Change `no-js` to `js` (independently of the `enableClasses` option) + // Handle classPrefix on this too + if (Modernizr._config.enableJSClass) { + var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); + className = className.replace(reJS, '$1' + classPrefix + 'js$2'); + } + + if (Modernizr._config.enableClasses) { + // Add the new classes + className += ' ' + classPrefix + classes.join(' ' + classPrefix); + isSVG ? docElement.className.baseVal = className : docElement.className = className; + } + + } + + ; + + /** + * createElement is a convenience wrapper around document.createElement. Since we + * use createElement all over the place, this allows for (slightly) smaller code + * as well as abstracting away issues with creating elements in contexts other than + * HTML documents (e.g. SVG documents). + * + * @access private + * @function createElement + * @returns {HTMLElement|SVGElement} An HTML or SVG element + */ + + function createElement() { + if (typeof document.createElement !== 'function') { + // This is the case in IE7, where the type of createElement is "object". + // For this reason, we cannot call apply() as Object is not a Function. + return document.createElement(arguments[0]); + } else if (isSVG) { + return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); + } else { + return document.createElement.apply(document, arguments); + } + } + + ; +/*! +{ + "name": "CSS rgba", + "caniuse": "css3-colors", + "property": "rgba", + "tags": ["css"], + "notes": [{ + "name": "CSSTricks Tutorial", + "href": "https://css-tricks.com/rgba-browser-support/" + }] +} +!*/ + + Modernizr.addTest('rgba', function() { + var style = createElement('a').style; + style.cssText = 'background-color:rgba(150,255,150,.5)'; + + return ('' + style.backgroundColor).indexOf('rgba') > -1; + }); + + + /** + * If the browsers follow the spec, then they would expose vendor-specific style as: + * elem.style.WebkitBorderRadius + * instead of something like the following, which would be technically incorrect: + * elem.style.webkitBorderRadius + + * Webkit ghosts their properties in lowercase but Opera & Moz do not. + * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ + * erik.eae.net/archives/2008/03/10/21.48.10/ + + * More here: github.com/Modernizr/Modernizr/issues/issue/21 + * + * @access private + * @returns {string} The string representing the vendor-specific style properties + */ + + var omPrefixes = 'Moz O ms Webkit'; + + + var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); + ModernizrProto._cssomPrefixes = cssomPrefixes; + + + /** + * List of JavaScript DOM values used for tests + * + * @memberof Modernizr + * @name Modernizr._domPrefixes + * @optionName Modernizr._domPrefixes + * @optionProp domPrefixes + * @access public + * @example + * + * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather + * than kebab-case properties, all properties are their Capitalized variant + * + * ```js + * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; + * ``` + */ + + var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); + ModernizrProto._domPrefixes = domPrefixes; + + + + /** + * contains checks to see if a string contains another string + * + * @access private + * @function contains + * @param {string} str - The string we want to check for substrings + * @param {string} substr - The substring we want to search the first string for + * @returns {boolean} + */ + + function contains(str, substr) { + return !!~('' + str).indexOf(substr); + } + + ; + + /** + * cssToDOM takes a kebab-case string and converts it to camelCase + * e.g. box-sizing -> boxSizing + * + * @access private + * @function cssToDOM + * @param {string} name - String name of kebab-case prop we want to convert + * @returns {string} The camelCase version of the supplied name + */ + + function cssToDOM(name) { + return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { + return m1 + m2.toUpperCase(); + }).replace(/^-/, ''); + } + ; + + /** + * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. + * + * @access private + * @function fnBind + * @param {function} fn - a function you want to change `this` reference to + * @param {object} that - the `this` you want to call the function with + * @returns {function} The wrapped version of the supplied function + */ + + function fnBind(fn, that) { + return function() { + return fn.apply(that, arguments); + }; + } + + ; + + /** + * testDOMProps is a generic DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + * + * @access private + * @function testDOMProps + * @param {array.} props - An array of properties to test for + * @param {object} obj - An object or Element you want to use to test the parameters again + * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check + */ + function testDOMProps(props, obj, elem) { + var item; + + for (var i in props) { + if (props[i] in obj) { + + // return the property name as a string + if (elem === false) { + return props[i]; + } + + item = obj[props[i]]; + + // let's bind a function + if (is(item, 'function')) { + // bind to obj unless overriden + return fnBind(item, elem || obj); + } + + // return the unbound function or obj or value + return item; + } + } + return false; + } + + ; + + /** + * Create our "modernizr" element that we do most feature tests on. + * + * @access private + */ + + var modElem = { + elem: createElement('modernizr') + }; + + // Clean up this element + Modernizr._q.push(function() { + delete modElem.elem; + }); + + + + var mStyle = { + style: modElem.elem.style + }; + + // kill ref for gc, must happen before mod.elem is removed, so we unshift on to + // the front of the queue. + Modernizr._q.unshift(function() { + delete mStyle.style; + }); + + + + /** + * domToCSS takes a camelCase string and converts it to kebab-case + * e.g. boxSizing -> box-sizing + * + * @access private + * @function domToCSS + * @param {string} name - String name of camelCase prop we want to convert + * @returns {string} The kebab-case version of the supplied name + */ + + function domToCSS(name) { + return name.replace(/([A-Z])/g, function(str, m1) { + return '-' + m1.toLowerCase(); + }).replace(/^ms-/, '-ms-'); + } + ; + + /** + * getBody returns the body of a document, or an element that can stand in for + * the body if a real body does not exist + * + * @access private + * @function getBody + * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an + * artificially created element that stands in for the body + */ + + function getBody() { + // After page load injecting a fake body doesn't work so check if body exists + var body = document.body; + + if (!body) { + // Can't use the real body create a fake one. + body = createElement(isSVG ? 'svg' : 'body'); + body.fake = true; + } + + return body; + } + + ; + + /** + * injectElementWithStyles injects an element with style element and some CSS rules + * + * @access private + * @function injectElementWithStyles + * @param {string} rule - String representing a css rule + * @param {function} callback - A function that is used to test the injected element + * @param {number} [nodes] - An integer representing the number of additional nodes you want injected + * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes + * @returns {boolean} + */ + + function injectElementWithStyles(rule, callback, nodes, testnames) { + var mod = 'modernizr'; + var style; + var ret; + var node; + var docOverflow; + var div = createElement('div'); + var body = getBody(); + + if (parseInt(nodes, 10)) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while (nodes--) { + node = createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + style = createElement('style'); + style.type = 'text/css'; + style.id = 's' + mod; + + // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (!body.fake ? div : body).appendChild(style); + body.appendChild(div); + + if (style.styleSheet) { + style.styleSheet.cssText = rule; + } else { + style.appendChild(document.createTextNode(rule)); + } + div.id = mod; + + if (body.fake) { + //avoid crashing IE8, if background image is used + body.style.background = ''; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + body.style.overflow = 'hidden'; + docOverflow = docElement.style.overflow; + docElement.style.overflow = 'hidden'; + docElement.appendChild(body); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if (body.fake) { + body.parentNode.removeChild(body); + docElement.style.overflow = docOverflow; + // Trigger layout so kinetic scrolling isn't disabled in iOS6+ + docElement.offsetHeight; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + + } + + ; + + /** + * nativeTestProps allows for us to use native feature detection functionality if available. + * some prefixed form, or false, in the case of an unsupported rule + * + * @access private + * @function nativeTestProps + * @param {array} props - An array of property names + * @param {string} value - A string representing the value we want to check via @supports + * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise + */ + + // Accepts a list of property names and a single value + // Returns `undefined` if native detection not available + function nativeTestProps(props, value) { + var i = props.length; + // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface + if ('CSS' in window && 'supports' in window.CSS) { + // Try every prefixed variant of the property + while (i--) { + if (window.CSS.supports(domToCSS(props[i]), value)) { + return true; + } + } + return false; + } + // Otherwise fall back to at-rule (for Opera 12.x) + else if ('CSSSupportsRule' in window) { + // Build a condition string for every prefixed variant + var conditionText = []; + while (i--) { + conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); + } + conditionText = conditionText.join(' or '); + return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { + return getComputedStyle(node, null).position == 'absolute'; + }); + } + return undefined; + } + ; + + // testProps is a generic CSS / DOM property test. + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + // Property names can be provided in either camelCase or kebab-case. + + function testProps(props, prefixed, value, skipValueTest) { + skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; + + // Try native detect first + if (!is(value, 'undefined')) { + var result = nativeTestProps(props, value); + if (!is(result, 'undefined')) { + return result; + } + } + + // Otherwise do it properly + var afterInit, i, propsLength, prop, before; + + // If we don't have a style element, that means we're running async or after + // the core tests, so we'll need to create our own elements to use + + // inside of an SVG element, in certain browsers, the `style` element is only + // defined for valid tags. Therefore, if `modernizr` does not have one, we + // fall back to a less used element and hope for the best. + var elems = ['modernizr', 'tspan']; + while (!mStyle.style) { + afterInit = true; + mStyle.modElem = createElement(elems.shift()); + mStyle.style = mStyle.modElem.style; + } + + // Delete the objects if we created them. + function cleanElems() { + if (afterInit) { + delete mStyle.style; + delete mStyle.modElem; + } + } + + propsLength = props.length; + for (i = 0; i < propsLength; i++) { + prop = props[i]; + before = mStyle.style[prop]; + + if (contains(prop, '-')) { + prop = cssToDOM(prop); + } + + if (mStyle.style[prop] !== undefined) { + + // If value to test has been passed in, do a set-and-check test. + // 0 (integer) is a valid property value, so check that `value` isn't + // undefined, rather than just checking it's truthy. + if (!skipValueTest && !is(value, 'undefined')) { + + // Needs a try catch block because of old IE. This is slow, but will + // be avoided in most cases because `skipValueTest` will be used. + try { + mStyle.style[prop] = value; + } catch (e) {} + + // If the property value has changed, we assume the value used is + // supported. If `value` is empty string, it'll fail here (because + // it hasn't changed), which matches how browsers have implemented + // CSS.supports() + if (mStyle.style[prop] != before) { + cleanElems(); + return prefixed == 'pfx' ? prop : true; + } + } + // Otherwise just return true, or the property name if this is a + // `prefixed()` call + else { + cleanElems(); + return prefixed == 'pfx' ? prop : true; + } + } + } + cleanElems(); + return false; + } + + ; + + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + * + * @access private + * @function testPropsAll + * @param {string} prop - A string of the property to test for + * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip + * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against + * @param {string} [value] - A string of a css value + * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set + */ + function testPropsAll(prop, prefixed, elem, value, skipValueTest) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); + + // did they call .prefixed('boxSizing') or are we just testing a prop? + if (is(prefixed, 'string') || is(prefixed, 'undefined')) { + return testProps(props, prefixed, value, skipValueTest); + + // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) + } else { + props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); + return testDOMProps(props, prefixed, elem); + } + } + + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + ModernizrProto.testAllProps = testPropsAll; + + + + /** + * testAllProps determines whether a given CSS property is supported in the browser + * + * @memberof Modernizr + * @name Modernizr.testAllProps + * @optionName Modernizr.testAllProps() + * @optionProp testAllProps + * @access public + * @function testAllProps + * @param {string} prop - String naming the property to test (either camelCase or kebab-case) + * @param {string} [value] - String of the value to test + * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection + * @example + * + * testAllProps determines whether a given CSS property, in some prefixed form, + * is supported by the browser. + * + * ```js + * testAllProps('boxSizing') // true + * ``` + * + * It can optionally be given a CSS value in string form to test if a property + * value is valid + * + * ```js + * testAllProps('display', 'block') // true + * testAllProps('display', 'penguin') // false + * ``` + * + * A boolean can be passed as a third parameter to skip the value check when + * native detection (@supports) isn't available. + * + * ```js + * testAllProps('shapeOutside', 'content-box', true); + * ``` + */ + + function testAllProps(prop, value, skipValueTest) { + return testPropsAll(prop, undefined, undefined, value, skipValueTest); + } + ModernizrProto.testAllProps = testAllProps; + +/*! +{ + "name": "CSS Animations", + "property": "cssanimations", + "caniuse": "css-animation", + "polyfills": ["transformie", "csssandpaper"], + "tags": ["css"], + "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], + "notes": [{ + "name" : "Article: 'Dispelling the Android CSS animation myths'", + "href": "https://goo.gl/OGw5Gm" + }] +} +!*/ +/* DOC +Detects whether or not elements can be animated using CSS +*/ + + Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); + +/*! +{ + "name": "CSS Transitions", + "property": "csstransitions", + "caniuse": "css-transitions", + "tags": ["css"] +} +!*/ + + Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); + + + // Run each test + testRunner(); + + // Remove the "no-js" class if it exists + setClasses(classes); + + delete ModernizrProto.addTest; + delete ModernizrProto.addAsyncTest; + + // Run the things that are supposed to run after the tests + for (var i = 0; i < Modernizr._q.length; i++) { + Modernizr._q[i](); + } + + // Leak Modernizr namespace + window.Modernizr = Modernizr; + + +; + +})(window, document); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 1190cb052..161473dbd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,6 +6,7 @@ var module.exports = { entry: { + 'boot': __dirname + '/dev/boot.jsx', 'app': __dirname + '/dev/app.jsx', 'admin': __dirname + '/dev/admin.jsx' }, @@ -22,7 +23,7 @@ module.exports = { new webpack.optimize.OccurenceOrderPlugin() ], resolve: { - root: path.resolve(__dirname, 'dev'), + root: [path.resolve(__dirname, 'dev'), path.resolve(__dirname, 'vendors')], extensions: ['', '.js', '.jsx'], alias: { 'Opentip': __dirname + '/dev/External/Opentip.js',