From 02e78b9d4d7b9356ced7c3fa9218a1cfd4122d96 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Fri, 20 Dec 2024 16:36:11 +0100 Subject: [PATCH 01/19] Add skiko with version No special error for compose wasm, because we have only one compose server --- src/config.js | 8 ++++---- src/webdemo-api.js | 17 ----------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/config.js b/src/config.js index 47cf90ad..3e872051 100644 --- a/src/config.js +++ b/src/config.js @@ -62,11 +62,11 @@ export const API_URLS = { get VERSIONS() { return `${this.server}/versions`; }, - SKIKO_MJS() { - return `${this.composeServer}/api/resource/skiko.mjs`; + SKIKO_MJS(version) { + return `${this.composeServer}/api/resource/skiko-${version}.mjs`; }, - SKIKO_WASM() { - return `${this.composeServer}/api/resource/skiko.wasm`; + SKIKO_WASM(version) { + return `${this.composeServer}/api/resource/skiko-${version}.wasm`; }, get JQUERY() { return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`; diff --git a/src/webdemo-api.js b/src/webdemo-api.js index 3b7514bc..64bade9d 100644 --- a/src/webdemo-api.js +++ b/src/webdemo-api.js @@ -59,7 +59,6 @@ export default class WebDemoApi { const MINIMAL_VERSION_IR = '1.5.0'; const MINIMAL_VERSION_WASM = '1.9.0'; const MINIMAL_VERSION_SWIFT_EXPORT = '2.0.0'; - const MAX_VERSION_COMPOSE_EXPORT = '2.0.0'; if ( platform === TargetPlatforms.JS_IR && @@ -90,22 +89,6 @@ export default class WebDemoApi { }); } - if ( - platform === TargetPlatforms.COMPOSE_WASM && - compilerVersion >= MAX_VERSION_COMPOSE_EXPORT - ) { - return Promise.resolve({ - output: '', - errors: [ - { - severity: 'ERROR', - message: `${TargetPlatforms.COMPOSE_WASM.printableName} compiler backend accessible only less ${MAX_VERSION_COMPOSE_EXPORT} version`, - }, - ], - jsCode: '', - }); - } - if ( platform === TargetPlatforms.SWIFT_EXPORT && compilerVersion < MINIMAL_VERSION_SWIFT_EXPORT From d69d3b29ddc1c226ec0001dee2d5de425d343cd8 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Fri, 20 Dec 2024 17:10:42 +0100 Subject: [PATCH 02/19] Add diff pixel ratio for screenshot comparison --- tests/utils/screenshots.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/utils/screenshots.ts b/tests/utils/screenshots.ts index e4de2c15..2bf3cf98 100644 --- a/tests/utils/screenshots.ts +++ b/tests/utils/screenshots.ts @@ -9,8 +9,14 @@ export async function hideCursor(node: Locator, callback: () => Promise) { await cursor.evaluate((element) => (element.style.display = null)); } +const MAX_DIFF_PIXEL_RATIO = 0.01; + export function checkScreenshot(node: Locator, message: string) { - return hideCursor(node, () => expect(node, message).toHaveScreenshot()); + return hideCursor(node, () => + expect(node, message).toHaveScreenshot({ + maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO, + }), + ); } export function checkEditorView(editor: Locator, message: string) { @@ -34,6 +40,9 @@ export function checkEditorView(editor: Locator, message: string) { height: boundingBox.height + margins.bottom, }; - await expect(editor.page(), message).toHaveScreenshot({ clip }); + await expect(editor.page(), message).toHaveScreenshot({ + clip, + maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO, + }); }); } From 02f6e5d890daffad6a126cab8a27f86ee8b66c3a Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Fri, 17 Jan 2025 15:16:35 +0100 Subject: [PATCH 03/19] Add support of 2-module Wasm compilation for Compose. It works like separate static stdlib bundle, and one small dynamic So we could instantiate stdlib with skiko, and then instantiate and run main bundle with ready stdlib. --- src/config.js | 12 ++- src/executable-code/executable-fragment.js | 7 +- src/js-executor/execute-es-module.js | 15 ++- src/js-executor/index.js | 119 ++++++++++++++++----- 4 files changed, 111 insertions(+), 42 deletions(-) diff --git a/src/config.js b/src/config.js index 3e872051..eb754d16 100644 --- a/src/config.js +++ b/src/config.js @@ -13,7 +13,7 @@ export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) }; export const API_URLS = { server: (RUNTIME_CONFIG.server || __WEBDEMO_URL__).replace(/\/$/, ''), composeServer: ( - 'https://compose.sandbox.intellij.net' || __WEBDEMO_URL__ + __WEBDEMO_URL__ ).replace(/\/$/, ''), COMPILE(platform, version) { @@ -63,10 +63,16 @@ export const API_URLS = { return `${this.server}/versions`; }, SKIKO_MJS(version) { - return `${this.composeServer}/api/resource/skiko-${version}.mjs`; + return `${this.composeServer}/api/resource/skiko.mjs`; }, SKIKO_WASM(version) { - return `${this.composeServer}/api/resource/skiko-${version}.wasm`; + return `${this.composeServer}/api/resource/skiko.wasm`; + }, + STDLIB_MJS(version) { + return `${this.composeServer}/api/resource/stdlib.mjs`; + }, + STDLIB_WASM(version) { + return `${this.composeServer}/api/resource/stdlib.wasm`; }, get JQUERY() { return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`; diff --git a/src/executable-code/executable-fragment.js b/src/executable-code/executable-fragment.js index fc1db0e2..3dc15722 100644 --- a/src/executable-code/executable-fragment.js +++ b/src/executable-code/executable-fragment.js @@ -413,8 +413,8 @@ export default class ExecutableFragment extends ExecutableCodeTemplate { ); const additionalRequests = []; if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - if (!this.jsExecutor.skikoImport) { - additionalRequests.push(this.jsExecutor.skikoImport); + if (this.jsExecutor.stdlibExports) { + additionalRequests.push(this.jsExecutor.stdlibExports); } } @@ -428,7 +428,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate { ), ...additionalRequests, ]).then( - ([state]) => { + ([state, ...additionalRequestsResults]) => { state.waitingForOutput = false; const jsCode = state.jsCode; const wasm = state.wasm; @@ -453,6 +453,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate { outputHeight, theme, onError, + additionalRequestsResults, ) .then((output) => { const originState = state.openConsole; diff --git a/src/js-executor/execute-es-module.js b/src/js-executor/execute-es-module.js index eb93dc82..30dfbd4f 100644 --- a/src/js-executor/execute-es-module.js +++ b/src/js-executor/execute-es-module.js @@ -3,17 +3,16 @@ export async function executeWasmCode(container, jsCode, wasmCode) { return execute(container, newCode, wasmCode); } -export async function executeWasmCodeWithSkiko(container, jsCode, wasmCode) { - const newCode = prepareJsCode(jsCode) - .replaceAll( - "imports['./skiko.mjs']", - "window.skikoImports" - ); - return execute(container, newCode, wasmCode); +export async function executeWasmCodeWithSkiko(container, jsCode) { + return executeJs(container, prepareJsCode(jsCode)); +} + +export async function executeWasmCodeWithStdlib(container, jsCode, wasmCode) { + return execute(container, prepareJsCode(jsCode), wasmCode); } function execute(container, jsCode, wasmCode) { - container.wasmCode = Uint8Array.from(atob(wasmCode), c => c.charCodeAt(0)); + container.wasmCode = Uint8Array.fromBase64(wasmCode); return executeJs(container, jsCode); } diff --git a/src/js-executor/index.js b/src/js-executor/index.js index 0afa2896..b5bbf109 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -1,10 +1,10 @@ import './index.scss'; -import { API_URLS } from '../config'; -import { showJsException } from '../view/output-view'; -import { processingHtmlBrackets } from '../utils'; -import { isWasmRelated, TargetPlatforms } from '../utils/platforms'; -import { executeJs, executeWasmCode, executeWasmCodeWithSkiko } from './execute-es-module'; -import { fetch } from "whatwg-fetch"; +import {API_URLS} from '../config'; +import {showJsException} from '../view/output-view'; +import {processingHtmlBrackets} from '../utils'; +import {isWasmRelated, TargetPlatforms} from '../utils/platforms'; +import {executeJs, executeWasmCode, executeWasmCodeWithSkiko, executeWasmCodeWithStdlib} from './execute-es-module'; +import {fetch} from "whatwg-fetch"; const INIT_SCRIPT = 'if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}' + @@ -26,7 +26,7 @@ const normalizeJsVersion = (version) => { export default class JsExecutor { constructor(kotlinVersion) { this.kotlinVersion = kotlinVersion; - this.skikoImport = undefined; + this.stdlibExports = undefined; } async executeJsCode( @@ -37,6 +37,7 @@ export default class JsExecutor { outputHeight, theme, onError, + additionalRequestsResults, ) { if (platform === TargetPlatforms.SWIFT_EXPORT) { return `
${jsCode}`; @@ -68,12 +69,15 @@ export default class JsExecutor { // for some reason resize function in Compose does not work in Firefox in invisible block this.iframe.style.display = 'block'; + const additionalRequestsResult = additionalRequestsResults[0]; const result = await this.executeWasm( jsCode, wasm, - executeWasmCodeWithSkiko, + executeWasmCodeWithStdlib, theme, processError, + additionalRequestsResult.stdlib, + additionalRequestsResult.output, ); if (exception) { @@ -106,8 +110,8 @@ export default class JsExecutor { const output = this.iframe.contentWindow.eval(jsCode); return output ? `${processingHtmlBrackets( - output, - )}` + output, + )}` : ''; } catch (e) { if (onError) onError(); @@ -119,20 +123,21 @@ export default class JsExecutor { return await this.execute(jsCode, jsLibs, theme, onError, platform); } - async executeWasm(jsCode, wasmCode, executor, theme, onError) { + async executeWasm(jsCode, wasmCode, executor, theme, onError, imports, output) { try { const exports = await executor( this.iframe.contentWindow, jsCode, wasmCode, ); - await exports.instantiate(); - const output = exports.bufferedOutput.buffer; - exports.bufferedOutput.buffer = ''; - return output + await exports.instantiate(imports); + const bufferedOutput = output ?? exports.bufferedOutput; + const outputString = bufferedOutput.buffer; + bufferedOutput.buffer = ''; + return outputString ? `${processingHtmlBrackets( - output, - )}` + outputString, + )}` : ''; } catch (e) { if (onError) onError(); @@ -177,27 +182,58 @@ export default class JsExecutor { } } if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - this.skikoImport = fetch(API_URLS.SKIKO_MJS(compilerVersion), { + + const skikoExports = fetch(API_URLS.SKIKO_MJS(compilerVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', } - }) - .then(script => script.text()) + }).then(script => script.text()) .then(script => script.replace( "new URL(\"skiko.wasm\",import.meta.url).href", `'${API_URLS.SKIKO_WASM(compilerVersion)}'` )) - .then(async skikoCode => { - return await executeJs( - this.iframe.contentWindow, - skikoCode, - ); + .then(async skikoCode => + executeJs( + this.iframe.contentWindow, + skikoCode, + )) + .then(skikoExports => fixedSkikoExports(skikoExports)) + + const stdlibExports = fetch(API_URLS.STDLIB_MJS(compilerVersion), { + method: 'GET', + headers: { + 'Content-Type': 'text/javascript', + } + }).then(script => script.text()) + .then(script => script.replace( + "new URL('./stdlib.wasm',import.meta.url).href", + `'${API_URLS.STDLIB_WASM(compilerVersion)}'` + )) + .then(stdlibCode => + executeWasmCodeWithSkiko( + this.iframe.contentWindow, + stdlibCode, + ) + ) + + this.stdlibExports = Promise.all([skikoExports, stdlibExports]) + .then(async ([skikoExportsResult, stdlibExportsResult]) => { + return [ + await stdlibExportsResult.stdlib({ + "./skiko.mjs": skikoExportsResult + }), + stdlibExportsResult + ] + } + ) + .then(([stdlibResult, outputResult]) => { + return { + "stdlib": stdlibResult.exports, + "output": outputResult.bufferedOutput + } } ) - .then(skikoImports => { - this.iframe.contentWindow.skikoImports = skikoImports; - }); this.iframe.height = "1000" iframeDoc.write(``); @@ -206,3 +242,30 @@ export default class JsExecutor { iframeDoc.close(); } } + +function fixedSkikoExports(skikoExports) { + return { + ...skikoExports, + org_jetbrains_skia_Bitmap__1nGetPixmap: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_Bitmap__1nIsVolatile: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_Bitmap__1nSetVolatile: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_TextBlobBuilderRunHandler__1nMake: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_TextBlobBuilderRunHandler__1nMakeBlob: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + }, + org_jetbrains_skia_svg_SVGCanvasKt__1nMake: function () { + console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + } + } +} From 5d8241c05e371636bd3f3ccf98d36fffdc65c8e8 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 21 Jan 2025 18:28:06 +0100 Subject: [PATCH 04/19] Add resources url --- package.json | 2 +- src/config.js | 23 +++++++++---- src/js-executor/index.js | 74 +++++++++++++++++++++++----------------- webpack.config.js | 2 ++ 4 files changed, 62 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index b574ce27..ac563212 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "copy-examples": "node utils/copy-examples", "release:ci": "rm -rf dist && npm run build:all && $NPM_TOKEN=%env.NPM_TOKEN% npm publish", "start": "webpack-dev-server --port 9002", - "start-with-local-compiler": "webpack-dev-server --port 9002 --env webDemoUrl='//localhost:8080'", + "start-with-local-compiler": "webpack-dev-server --port 9002 --env webDemoUrl='//localhost:8080' webDemoResourcesUrl='//localhost:8081'", "lint": "eslint . --ext .ts", "fix": "eslint --fix --ext .ts .", "test": "npm run build:all && npm run test:run", diff --git a/src/config.js b/src/config.js index eb754d16..fde12e88 100644 --- a/src/config.js +++ b/src/config.js @@ -13,7 +13,10 @@ export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) }; export const API_URLS = { server: (RUNTIME_CONFIG.server || __WEBDEMO_URL__).replace(/\/$/, ''), composeServer: ( - __WEBDEMO_URL__ + __WEBDEMO_URL__ || 'https://compose.sandbox.intellij.net' + ).replace(/\/$/, ''), + composeResources: ( + __WEBDEMO_RESOURCES_URL__ || 'https://compose.sandbox.intellij.net' ).replace(/\/$/, ''), COMPILE(platform, version) { @@ -62,17 +65,23 @@ export const API_URLS = { get VERSIONS() { return `${this.server}/versions`; }, + SKIKO_VERSION() { + return `${this.composeServer}/api/resource/skiko`; + }, SKIKO_MJS(version) { - return `${this.composeServer}/api/resource/skiko.mjs`; + return `${this.composeResources}/api/resource/skiko-${version}.mjs`; }, SKIKO_WASM(version) { - return `${this.composeServer}/api/resource/skiko.wasm`; + return `${this.composeResources}/api/resource/skiko-${version}.wasm`; + }, + STDLIB_HASH() { + return `${this.composeServer}/api/resource/stdlib`; }, - STDLIB_MJS(version) { - return `${this.composeServer}/api/resource/stdlib.mjs`; + STDLIB_MJS(hash) { + return `${this.composeResources}/api/resource/stdlib-${hash}.mjs`; }, - STDLIB_WASM(version) { - return `${this.composeServer}/api/resource/stdlib.wasm`; + STDLIB_WASM(hash) { + return `${this.composeResources}/api/resource/stdlib-${hash}.wasm`; }, get JQUERY() { return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`; diff --git a/src/js-executor/index.js b/src/js-executor/index.js index b5bbf109..aa76a2a0 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -183,38 +183,50 @@ export default class JsExecutor { } if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - const skikoExports = fetch(API_URLS.SKIKO_MJS(compilerVersion), { - method: 'GET', - headers: { - 'Content-Type': 'text/javascript', - } - }).then(script => script.text()) - .then(script => script.replace( - "new URL(\"skiko.wasm\",import.meta.url).href", - `'${API_URLS.SKIKO_WASM(compilerVersion)}'` - )) - .then(async skikoCode => - executeJs( - this.iframe.contentWindow, - skikoCode, - )) - .then(skikoExports => fixedSkikoExports(skikoExports)) + const skikoExports = fetch(API_URLS.SKIKO_VERSION(), { + method: 'GET' + }).then(response => response.text()) + .then(version => + fetch(API_URLS.SKIKO_MJS(version), { + method: 'GET', + headers: { + 'Content-Type': 'text/javascript', + } + }).then(script => script.text()) + .then(script => script.replace( + "new URL(\"skiko.wasm\",import.meta.url).href", + `'${API_URLS.SKIKO_WASM(version)}'` + )) + .then(async skikoCode => + executeJs( + this.iframe.contentWindow, + skikoCode, + )) + .then(skikoExports => fixedSkikoExports(skikoExports))) - const stdlibExports = fetch(API_URLS.STDLIB_MJS(compilerVersion), { - method: 'GET', - headers: { - 'Content-Type': 'text/javascript', - } - }).then(script => script.text()) - .then(script => script.replace( - "new URL('./stdlib.wasm',import.meta.url).href", - `'${API_URLS.STDLIB_WASM(compilerVersion)}'` - )) - .then(stdlibCode => - executeWasmCodeWithSkiko( - this.iframe.contentWindow, - stdlibCode, - ) + const stdlibExports = fetch(API_URLS.STDLIB_HASH(), { + method: 'GET' + }).then(response => response.text()) + .then(hash => + fetch(API_URLS.STDLIB_MJS(hash), { + method: 'GET', + headers: { + 'Content-Type': 'text/javascript', + } + }).then(script => script.text()) + .then(script => script.replace( + "new URL('./stdlib.wasm',import.meta.url).href", + `'${API_URLS.STDLIB_WASM(hash)}'` + ).replace( + "(extends) => { return { extends }; }", + "(extends_) => { return { extends_ }; }" + )) + .then(stdlibCode => + executeWasmCodeWithSkiko( + this.iframe.contentWindow, + stdlibCode, + ) + ) ) this.stdlibExports = Promise.all([skikoExports, stdlibExports]) diff --git a/webpack.config.js b/webpack.config.js index f2b1cf69..c011108a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,6 +9,7 @@ module.exports = (params = {}) => { const isServer = process.argv[1].includes('webpack-dev-server'); const libraryName = 'KotlinPlayground'; const webDemoUrl = params.webDemoUrl || 'https://api.kotlinlang.org/'; + const webDemoResourcesUrl = params.webDemoResourcesUrl || 'https://api.kotlinlang.org/'; const examplesPath = isServer ? '' : 'examples/'; const pathDist = path.resolve(__dirname, 'dist'); @@ -98,6 +99,7 @@ module.exports = (params = {}) => { new webpack.DefinePlugin({ __WEBDEMO_URL__: JSON.stringify(webDemoUrl), + __WEBDEMO_RESOURCES_URL__: JSON.stringify(webDemoResourcesUrl), __IS_PRODUCTION__: isProduction, __LIBRARY_NAME__: JSON.stringify(libraryName), }), From 402e921fc5e8e81d6898bfb1ae970910b503d389 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 21 Jan 2025 18:42:33 +0100 Subject: [PATCH 05/19] Parallelize stdlib loading --- src/js-executor/index.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/js-executor/index.js b/src/js-executor/index.js index aa76a2a0..8ba1f45f 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -197,7 +197,7 @@ export default class JsExecutor { "new URL(\"skiko.wasm\",import.meta.url).href", `'${API_URLS.SKIKO_WASM(version)}'` )) - .then(async skikoCode => + .then(skikoCode => executeJs( this.iframe.contentWindow, skikoCode, @@ -214,13 +214,16 @@ export default class JsExecutor { 'Content-Type': 'text/javascript', } }).then(script => script.text()) - .then(script => script.replace( - "new URL('./stdlib.wasm',import.meta.url).href", - `'${API_URLS.STDLIB_WASM(hash)}'` - ).replace( - "(extends) => { return { extends }; }", - "(extends_) => { return { extends_ }; }" - )) + .then(script => + // necessary to load stdlib.wasm before its initialization to parallelize + // language=JavaScript + (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(hash)}');\n` + script).replace( + "new URL('./stdlib.wasm',import.meta.url).href", + "stdlibWasm" + ).replace( + "(extends) => { return { extends }; }", + "(extends_) => { return { extends_ }; }" + )) .then(stdlibCode => executeWasmCodeWithSkiko( this.iframe.contentWindow, From 376fda350d2cfa19c40dd0a83e6a8a4ea36754f8 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 21 Jan 2025 19:57:29 +0100 Subject: [PATCH 06/19] Fix --- src/js-executor/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js-executor/index.js b/src/js-executor/index.js index 8ba1f45f..f59cf58c 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -218,7 +218,7 @@ export default class JsExecutor { // necessary to load stdlib.wasm before its initialization to parallelize // language=JavaScript (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(hash)}');\n` + script).replace( - "new URL('./stdlib.wasm',import.meta.url).href", + "fetch(new URL('./stdlib.wasm',import.meta.url).href)", "stdlibWasm" ).replace( "(extends) => { return { extends }; }", From d04a0d5aa246fae16fc61200886e1c263f9d843c Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 28 Jan 2025 15:30:46 +0100 Subject: [PATCH 07/19] No fromBase64 in Chrome yet --- src/js-executor/execute-es-module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js-executor/execute-es-module.js b/src/js-executor/execute-es-module.js index 30dfbd4f..3a49daf0 100644 --- a/src/js-executor/execute-es-module.js +++ b/src/js-executor/execute-es-module.js @@ -12,7 +12,7 @@ export async function executeWasmCodeWithStdlib(container, jsCode, wasmCode) { } function execute(container, jsCode, wasmCode) { - container.wasmCode = Uint8Array.fromBase64(wasmCode); + container.wasmCode = Uint8Array.from(atob(wasmCode), c => c.charCodeAt(0)); return executeJs(container, jsCode); } From a0e24c2451921f398328fb0dec7b1385ee3929b7 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Thu, 6 Mar 2025 16:00:47 +0100 Subject: [PATCH 08/19] Fix after version upgrade --- src/js-executor/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js-executor/index.js b/src/js-executor/index.js index f59cf58c..1bf17dce 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -130,7 +130,7 @@ export default class JsExecutor { jsCode, wasmCode, ); - await exports.instantiate(imports); + await exports.instantiate({"playground.master": imports}); const bufferedOutput = output ?? exports.bufferedOutput; const outputString = bufferedOutput.buffer; bufferedOutput.buffer = ''; @@ -218,7 +218,7 @@ export default class JsExecutor { // necessary to load stdlib.wasm before its initialization to parallelize // language=JavaScript (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(hash)}');\n` + script).replace( - "fetch(new URL('./stdlib.wasm',import.meta.url).href)", + "fetch(new URL('./stdlib_master.wasm',import.meta.url).href)", "stdlibWasm" ).replace( "(extends) => { return { extends }; }", @@ -235,7 +235,7 @@ export default class JsExecutor { this.stdlibExports = Promise.all([skikoExports, stdlibExports]) .then(async ([skikoExportsResult, stdlibExportsResult]) => { return [ - await stdlibExportsResult.stdlib({ + await stdlibExportsResult.instantiate({ "./skiko.mjs": skikoExportsResult }), stdlibExportsResult From c003bf68cf399bf3fe28b7b31c3ffa6b5e06de7a Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Fri, 7 Mar 2025 15:06:58 +0100 Subject: [PATCH 09/19] Use one request for compose wasm resource versions --- src/config.js | 7 ++----- src/js-executor/index.js | 30 ++++++++++++++++-------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/config.js b/src/config.js index fde12e88..7924ba42 100644 --- a/src/config.js +++ b/src/config.js @@ -65,8 +65,8 @@ export const API_URLS = { get VERSIONS() { return `${this.server}/versions`; }, - SKIKO_VERSION() { - return `${this.composeServer}/api/resource/skiko`; + RESOURCE_VERSIONS() { + return `${this.composeServer}/api/resource/compose-wasm-versions`; }, SKIKO_MJS(version) { return `${this.composeResources}/api/resource/skiko-${version}.mjs`; @@ -74,9 +74,6 @@ export const API_URLS = { SKIKO_WASM(version) { return `${this.composeResources}/api/resource/skiko-${version}.wasm`; }, - STDLIB_HASH() { - return `${this.composeServer}/api/resource/stdlib`; - }, STDLIB_MJS(hash) { return `${this.composeResources}/api/resource/stdlib-${hash}.mjs`; }, diff --git a/src/js-executor/index.js b/src/js-executor/index.js index 1bf17dce..2998510c 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -183,11 +183,13 @@ export default class JsExecutor { } if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - const skikoExports = fetch(API_URLS.SKIKO_VERSION(), { + const skikoStdlib = fetch(API_URLS.RESOURCE_VERSIONS(),{ method: 'GET' - }).then(response => response.text()) - .then(version => - fetch(API_URLS.SKIKO_MJS(version), { + }).then(response => response.json()) + .then(versions => { + const skikoVersion = versions["skiko"]; + + const skikoExports = fetch(API_URLS.SKIKO_MJS(skikoVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', @@ -195,20 +197,18 @@ export default class JsExecutor { }).then(script => script.text()) .then(script => script.replace( "new URL(\"skiko.wasm\",import.meta.url).href", - `'${API_URLS.SKIKO_WASM(version)}'` + `'${API_URLS.SKIKO_WASM(skikoVersion)}'` )) .then(skikoCode => executeJs( this.iframe.contentWindow, skikoCode, )) - .then(skikoExports => fixedSkikoExports(skikoExports))) + .then(skikoExports => fixedSkikoExports(skikoExports)) - const stdlibExports = fetch(API_URLS.STDLIB_HASH(), { - method: 'GET' - }).then(response => response.text()) - .then(hash => - fetch(API_URLS.STDLIB_MJS(hash), { + const stdlibVersion = versions["stdlib"]; + + const stdlibExports = fetch(API_URLS.STDLIB_MJS(stdlibVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', @@ -217,7 +217,7 @@ export default class JsExecutor { .then(script => // necessary to load stdlib.wasm before its initialization to parallelize // language=JavaScript - (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(hash)}');\n` + script).replace( + (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(stdlibVersion)}');\n` + script).replace( "fetch(new URL('./stdlib_master.wasm',import.meta.url).href)", "stdlibWasm" ).replace( @@ -230,9 +230,11 @@ export default class JsExecutor { stdlibCode, ) ) - ) - this.stdlibExports = Promise.all([skikoExports, stdlibExports]) + return Promise.all([skikoExports, stdlibExports]) + }) + + this.stdlibExports = skikoStdlib .then(async ([skikoExportsResult, stdlibExportsResult]) => { return [ await stdlibExportsResult.instantiate({ From 9307adfc461928a269c9cd7f0256b5b6d47ed0ca Mon Sep 17 00:00:00 2001 From: zoobestik Date: Thu, 6 Mar 2025 18:59:05 +0100 Subject: [PATCH 10/19] tmp --- src/config.js | 18 ++++++++---------- webpack.config.js | 10 +++++++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/config.js b/src/config.js index 7924ba42..86a5b5b6 100644 --- a/src/config.js +++ b/src/config.js @@ -12,12 +12,10 @@ export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) }; */ export const API_URLS = { server: (RUNTIME_CONFIG.server || __WEBDEMO_URL__).replace(/\/$/, ''), - composeServer: ( - __WEBDEMO_URL__ || 'https://compose.sandbox.intellij.net' - ).replace(/\/$/, ''), - composeResources: ( - __WEBDEMO_RESOURCES_URL__ || 'https://compose.sandbox.intellij.net' - ).replace(/\/$/, ''), + composeServer: 'https://compose-stage.sandbox.intellij.net'.replace( + /\/$/, + '', + ), COMPILE(platform, version) { let url; @@ -69,16 +67,16 @@ export const API_URLS = { return `${this.composeServer}/api/resource/compose-wasm-versions`; }, SKIKO_MJS(version) { - return `${this.composeResources}/api/resource/skiko-${version}.mjs`; + return `${this.composeServer}/api/resource/skiko-${version}.mjs`; }, SKIKO_WASM(version) { - return `${this.composeResources}/api/resource/skiko-${version}.wasm`; + return `${this.composeServer}/api/resource/skiko-${version}.wasm`; }, STDLIB_MJS(hash) { - return `${this.composeResources}/api/resource/stdlib-${hash}.mjs`; + return `${this.composeServer}/api/resource/stdlib-${hash}.mjs`; }, STDLIB_WASM(hash) { - return `${this.composeResources}/api/resource/stdlib-${hash}.wasm`; + return `${this.composeServer}/api/resource/stdlib-${hash}.wasm`; }, get JQUERY() { return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`; diff --git a/webpack.config.js b/webpack.config.js index c011108a..4314e0e8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,8 @@ module.exports = (params = {}) => { const isServer = process.argv[1].includes('webpack-dev-server'); const libraryName = 'KotlinPlayground'; const webDemoUrl = params.webDemoUrl || 'https://api.kotlinlang.org/'; - const webDemoResourcesUrl = params.webDemoResourcesUrl || 'https://api.kotlinlang.org/'; + const webDemoResourcesUrl = + params.webDemoResourcesUrl || 'https://api.kotlinlang.org/'; const examplesPath = isServer ? '' : 'examples/'; const pathDist = path.resolve(__dirname, 'dist'); @@ -106,6 +107,13 @@ module.exports = (params = {}) => { ], devServer: { static: path.resolve(__dirname, 'src'), + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': + 'GET, POST, PUT, DELETE, PATCH, OPTIONS', + 'Access-Control-Allow-Headers': + 'X-Requested-With, content-type, Authorization', + }, }, }; From 2a3037b3e05d3e0114310e661336eaff481fb20e Mon Sep 17 00:00:00 2001 From: zoobestik Date: Thu, 6 Mar 2025 19:00:48 +0100 Subject: [PATCH 11/19] 1.32.0-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac563212..adbe36ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kotlin-playground", - "version": "1.31.0-alfa11", + "version": "1.32.0-alpha.0", "description": "Self-contained component to embed in websites for running Kotlin code", "keywords": [ "kotlin", From ce1120528f711025a91da1f488ea3535dd6f167b Mon Sep 17 00:00:00 2001 From: zoobestik Date: Fri, 7 Mar 2025 15:29:16 +0100 Subject: [PATCH 12/19] 1.32.0-alpha.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adbe36ee..8eafdb94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kotlin-playground", - "version": "1.32.0-alpha.0", + "version": "1.32.0-alpha.1", "description": "Self-contained component to embed in websites for running Kotlin code", "keywords": [ "kotlin", From 280ab3d5be1b4ead5529098571e42dd7661dab91 Mon Sep 17 00:00:00 2001 From: zoobestik Date: Tue, 22 Apr 2025 22:17:42 +0200 Subject: [PATCH 13/19] release 1.32.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8eafdb94..1de4949d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kotlin-playground", - "version": "1.32.0-alpha.1", + "version": "1.32.0", "description": "Self-contained component to embed in websites for running Kotlin code", "keywords": [ "kotlin", From 623dadb8917b27e3fbc2b52f424e58e5e2ea3b20 Mon Sep 17 00:00:00 2001 From: zoobestik Date: Tue, 20 May 2025 13:16:44 +0200 Subject: [PATCH 14/19] feat: remove deprecated JS platform --- src/config.js | 3 - src/executable-code/executable-fragment.js | 4 +- src/js-executor/index.js | 216 +- src/utils/platforms/TargetPlatforms.ts | 1 - src/utils/platforms/index.ts | 14 +- src/webdemo-api.js | 17 - tests/restrictions.e2e.ts | 94 +- tests/utils/mocks/compiler.ts | 36 +- tests/utils/mocks/jquery.min.js | 5 + tests/utils/mocks/kotlin.js | 53216 +++++++++++++++++++ tests/utils/mocks/wasm/result.ts | 4 + tests/wasm-compatibility.e2e.ts | 11 +- 12 files changed, 53485 insertions(+), 136 deletions(-) create mode 100644 tests/utils/mocks/jquery.min.js create mode 100644 tests/utils/mocks/kotlin.js diff --git a/src/config.js b/src/config.js index 86a5b5b6..6faf9234 100644 --- a/src/config.js +++ b/src/config.js @@ -28,9 +28,6 @@ export const API_URLS = { url = `${this.server}/api/${version}/compiler/translate`; break; case TargetPlatforms.JS: - url = `${this.server}/api/${version}/compiler/translate`; - break; - case TargetPlatforms.JS_IR: url = `${this.server}/api/${version}/compiler/translate?ir=true`; break; case TargetPlatforms.WASM: diff --git a/src/executable-code/executable-fragment.js b/src/executable-code/executable-fragment.js index 3dc15722..c1cd477f 100644 --- a/src/executable-code/executable-fragment.js +++ b/src/executable-code/executable-fragment.js @@ -313,7 +313,8 @@ export default class ExecutableFragment extends ExecutableCodeTemplate { } onConsoleCloseButtonEnter() { - const { jsLibs, onCloseConsole, targetPlatform, compilerVersion } = this.state; + const { jsLibs, onCloseConsole, targetPlatform, compilerVersion } = + this.state; // creates a new iframe and removes the old one, thereby stops execution of any running script if (isJsRelated(targetPlatform) || isWasmRelated(targetPlatform)) this.jsExecutor.reloadIframeScripts( @@ -454,6 +455,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate { theme, onError, additionalRequestsResults, + compilerVersion, ) .then((output) => { const originState = state.openConsole; diff --git a/src/js-executor/index.js b/src/js-executor/index.js index 2998510c..fbfb98c0 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -1,10 +1,15 @@ import './index.scss'; -import {API_URLS} from '../config'; -import {showJsException} from '../view/output-view'; -import {processingHtmlBrackets} from '../utils'; -import {isWasmRelated, TargetPlatforms} from '../utils/platforms'; -import {executeJs, executeWasmCode, executeWasmCodeWithSkiko, executeWasmCodeWithStdlib} from './execute-es-module'; -import {fetch} from "whatwg-fetch"; +import { API_URLS } from '../config'; +import { showJsException } from '../view/output-view'; +import { processingHtmlBrackets } from '../utils'; +import { isJsLegacy, isWasmRelated, TargetPlatforms } from '../utils/platforms'; +import { + executeJs, + executeWasmCode, + executeWasmCodeWithSkiko, + executeWasmCodeWithStdlib, +} from './execute-es-module'; +import { fetch } from 'whatwg-fetch'; const INIT_SCRIPT = 'if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}' + @@ -38,6 +43,7 @@ export default class JsExecutor { theme, onError, additionalRequestsResults, + compilerVersion, ) { if (platform === TargetPlatforms.SWIFT_EXPORT) { return `
${jsCode}`; @@ -90,18 +96,22 @@ export default class JsExecutor { return result; } } - return await this.execute(jsCode, jsLibs, theme, onError, platform); + return await this.execute( + jsCode, + jsLibs, + theme, + onError, + platform, + compilerVersion, + ); } - async execute(jsCode, jsLibs, theme, onError, platform) { + async execute(jsCode, jsLibs, theme, onError, platform, compilerVersion) { const loadedScripts = ( this.iframe.contentDocument || this.iframe.document ).getElementsByTagName('script').length; - let offset; - if (platform === TargetPlatforms.JS_IR) { - // 1 scripts by default: INIT_SCRIPT_IR - offset = 1; - } else { + let offset = 1; // 1 scripts by default: INIT_SCRIPT_IR + if (isJsLegacy(platform, compilerVersion)) { // 2 scripts by default: INIT_SCRIPT + kotlin stdlib offset = 2; } @@ -110,8 +120,8 @@ export default class JsExecutor { const output = this.iframe.contentWindow.eval(jsCode); return output ? `${processingHtmlBrackets( - output, - )}` + output, + )}` : ''; } catch (e) { if (onError) onError(); @@ -120,24 +130,39 @@ export default class JsExecutor { } } await this.timeout(400); - return await this.execute(jsCode, jsLibs, theme, onError, platform); + return await this.execute( + jsCode, + jsLibs, + theme, + onError, + platform, + compilerVersion, + ); } - async executeWasm(jsCode, wasmCode, executor, theme, onError, imports, output) { + async executeWasm( + jsCode, + wasmCode, + executor, + theme, + onError, + imports, + output, + ) { try { const exports = await executor( this.iframe.contentWindow, jsCode, wasmCode, ); - await exports.instantiate({"playground.master": imports}); + await exports.instantiate({ 'playground.master': imports }); const bufferedOutput = output ?? exports.bufferedOutput; const outputString = bufferedOutput.buffer; bufferedOutput.buffer = ''; return outputString ? `${processingHtmlBrackets( - outputString, - )}` + outputString, + )}` : ''; } catch (e) { if (onError) onError(); @@ -159,10 +184,7 @@ export default class JsExecutor { node.appendChild(this.iframe); let iframeDoc = this.iframe.contentDocument || this.iframe.document; iframeDoc.open(); - if ( - targetPlatform === TargetPlatforms.JS || - targetPlatform === TargetPlatforms.CANVAS - ) { + if (isJsLegacy(targetPlatform, compilerVersion)) { const kotlinScript = API_URLS.KOTLIN_JS + `${normalizeJsVersion(this.kotlinVersion)}/kotlin.js`; @@ -175,84 +197,88 @@ export default class JsExecutor { for (let lib of jsLibs) { iframeDoc.write(""); } - if (targetPlatform === TargetPlatforms.JS_IR) { - iframeDoc.write(``); - } else { - iframeDoc.write(``); - } + iframeDoc.write( + ``, + ); } if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - - const skikoStdlib = fetch(API_URLS.RESOURCE_VERSIONS(),{ - method: 'GET' - }).then(response => response.json()) - .then(versions => { - const skikoVersion = versions["skiko"]; + const skikoStdlib = fetch(API_URLS.RESOURCE_VERSIONS(), { + method: 'GET', + }) + .then((response) => response.json()) + .then((versions) => { + const skikoVersion = versions['skiko']; const skikoExports = fetch(API_URLS.SKIKO_MJS(skikoVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', - } - }).then(script => script.text()) - .then(script => script.replace( - "new URL(\"skiko.wasm\",import.meta.url).href", - `'${API_URLS.SKIKO_WASM(skikoVersion)}'` - )) - .then(skikoCode => - executeJs( - this.iframe.contentWindow, - skikoCode, - )) - .then(skikoExports => fixedSkikoExports(skikoExports)) + }, + }) + .then((script) => script.text()) + .then((script) => + script.replace( + 'new URL("skiko.wasm",import.meta.url).href', + `'${API_URLS.SKIKO_WASM(skikoVersion)}'`, + ), + ) + .then((skikoCode) => + executeJs(this.iframe.contentWindow, skikoCode), + ) + .then((skikoExports) => fixedSkikoExports(skikoExports)); - const stdlibVersion = versions["stdlib"]; + const stdlibVersion = versions['stdlib']; const stdlibExports = fetch(API_URLS.STDLIB_MJS(stdlibVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', - } - }).then(script => script.text()) - .then(script => + }, + }) + .then((script) => script.text()) + .then((script) => // necessary to load stdlib.wasm before its initialization to parallelize // language=JavaScript - (`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(stdlibVersion)}');\n` + script).replace( - "fetch(new URL('./stdlib_master.wasm',import.meta.url).href)", - "stdlibWasm" - ).replace( - "(extends) => { return { extends }; }", - "(extends_) => { return { extends_ }; }" - )) - .then(stdlibCode => - executeWasmCodeWithSkiko( - this.iframe.contentWindow, - stdlibCode, + ( + `const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(stdlibVersion)}');\n` + + script ) + .replace( + "fetch(new URL('./stdlib_master.wasm',import.meta.url).href)", + 'stdlibWasm', + ) + .replace( + '(extends) => { return { extends }; }', + '(extends_) => { return { extends_ }; }', + ), ) + .then((stdlibCode) => + executeWasmCodeWithSkiko(this.iframe.contentWindow, stdlibCode), + ) + .then((stdlibCode) => + executeWasmCodeWithSkiko(this.iframe.contentWindow, stdlibCode), + ); - return Promise.all([skikoExports, stdlibExports]) - }) + return Promise.all([skikoExports, stdlibExports]); + }); this.stdlibExports = skikoStdlib .then(async ([skikoExportsResult, stdlibExportsResult]) => { - return [ - await stdlibExportsResult.instantiate({ - "./skiko.mjs": skikoExportsResult - }), - stdlibExportsResult - ] - } - ) + return [ + await stdlibExportsResult.instantiate({ + './skiko.mjs': skikoExportsResult, + }), + stdlibExportsResult, + ]; + }) .then(([stdlibResult, outputResult]) => { - return { - "stdlib": stdlibResult.exports, - "output": outputResult.bufferedOutput - } - } - ) + return { + stdlib: stdlibResult.exports, + output: outputResult.bufferedOutput, + }; + }); - this.iframe.height = "1000" + this.iframe.height = '1000'; iframeDoc.write(``); } iframeDoc.write(''); @@ -264,25 +290,39 @@ function fixedSkikoExports(skikoExports) { return { ...skikoExports, org_jetbrains_skia_Bitmap__1nGetPixmap: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_Bitmap__1nIsVolatile: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_Bitmap__1nSetVolatile: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_TextBlobBuilderRunHandler__1nMake: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_TextBlobBuilderRunHandler__1nMakeBlob: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); }, org_jetbrains_skia_svg_SVGCanvasKt__1nMake: function () { - console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer") - } - } + console.log( + 'org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer', + ); + }, + }; } diff --git a/src/utils/platforms/TargetPlatforms.ts b/src/utils/platforms/TargetPlatforms.ts index 970e6541..0adcfd5d 100644 --- a/src/utils/platforms/TargetPlatforms.ts +++ b/src/utils/platforms/TargetPlatforms.ts @@ -2,7 +2,6 @@ import TargetPlatform from './TargetPlatform'; export const TargetPlatforms = { JS: new TargetPlatform('js', 'JavaScript'), - JS_IR: new TargetPlatform('js-ir', 'JavaScript IR'), WASM: new TargetPlatform('wasm', 'Wasm'), COMPOSE_WASM: new TargetPlatform('compose-wasm', 'Compose Wasm'), JAVA: new TargetPlatform('java', 'JVM'), diff --git a/src/utils/platforms/index.ts b/src/utils/platforms/index.ts index 689d4128..feb11246 100644 --- a/src/utils/platforms/index.ts +++ b/src/utils/platforms/index.ts @@ -3,7 +3,12 @@ import { TargetPlatforms } from './TargetPlatforms'; import { isKeyOfObject } from '../types'; export function getTargetById(id?: string | null) { - const key = id && id.toUpperCase().replace(/-/g, '_'); + let key = id && id.toUpperCase().replace(/-/g, '_'); + + if (key === 'JS_IR') { + console.warn('JS_IR is deprecated, use JS + compiler-version instead'); + key = 'JS'; + } return isKeyOfObject(key, TargetPlatforms) ? TargetPlatforms[key] : null; } @@ -17,7 +22,6 @@ export function isJavaRelated(platform: TargetPlatform) { export function isJsRelated(platform: TargetPlatform) { return ( platform === TargetPlatforms.JS || - platform === TargetPlatforms.JS_IR || platform === TargetPlatforms.CANVAS || platform === TargetPlatforms.SWIFT_EXPORT ); @@ -30,4 +34,10 @@ export function isWasmRelated(platform: TargetPlatform) { ); } +const MINIMAL_VERSION_IR = '1.5.0'; + +export function isJsLegacy(platform: TargetPlatform, compilerVersion: string) { + return isJsRelated(platform) && compilerVersion < MINIMAL_VERSION_IR; +} + export * from './TargetPlatforms'; diff --git a/src/webdemo-api.js b/src/webdemo-api.js index 64bade9d..a2298e34 100644 --- a/src/webdemo-api.js +++ b/src/webdemo-api.js @@ -56,26 +56,9 @@ export default class WebDemoApi { args, hiddenDependencies, ) { - const MINIMAL_VERSION_IR = '1.5.0'; const MINIMAL_VERSION_WASM = '1.9.0'; const MINIMAL_VERSION_SWIFT_EXPORT = '2.0.0'; - if ( - platform === TargetPlatforms.JS_IR && - compilerVersion < MINIMAL_VERSION_IR - ) { - return Promise.resolve({ - output: '', - errors: [ - { - severity: 'ERROR', - message: `JS IR compiler backend accessible only since ${MINIMAL_VERSION_IR} version`, - }, - ], - jsCode: '', - }); - } - if (isWasmRelated(platform) && compilerVersion < MINIMAL_VERSION_WASM) { return Promise.resolve({ output: '', diff --git a/tests/restrictions.e2e.ts b/tests/restrictions.e2e.ts index 28a16bcb..804fc1c0 100644 --- a/tests/restrictions.e2e.ts +++ b/tests/restrictions.e2e.ts @@ -1,16 +1,24 @@ -import { expect, Page, test } from '@playwright/test'; - import { readFileSync } from 'fs'; import { join } from 'path'; -import { gotoHtmlWidget } from './utils/server/playground'; - -import { RESULT_SELECTOR, WIDGET_SELECTOR } from './utils/selectors'; +import { ConsoleMessage } from 'playwright-core'; +import { expect, Page, test } from '@playwright/test'; import { prepareNetwork, printlnCode } from './utils'; -import { mockRunRequest, waitRunRequest } from './utils/mocks/compiler'; +import { RESULT_SELECTOR, WIDGET_SELECTOR } from './utils/selectors'; +import { gotoHtmlWidget } from './utils/server/playground'; import { runButton } from './utils/interactions'; -import { makeJSPrintCode } from './utils/mocks/wasm/result'; + +import { + mockJsLegacyAssets, + mockRunRequest, + waitRunRequest, +} from './utils/mocks/compiler'; + +import { + makeJSLegacyPrintCode, + makeJSPrintCode, +} from './utils/mocks/wasm/result'; const WASM = JSON.parse( readFileSync(join(__dirname, 'utils/mocks/wasm/wasm.json'), 'utf-8'), @@ -23,9 +31,29 @@ const JS_IR = Object.freeze({ text: 'Hello, world!\n', }); +const JS_LEGACY = Object.freeze({ + jsCode: makeJSLegacyPrintCode('Hello, world!'), + errors: { 'File.kt': [] }, + exception: null, + text: 'Hello, world!\n', +}); + const OUTPUTS = Object.freeze({ - 'js-ir': JS_IR, - wasm: WASM, + 'js-ir': { + '1.3.10': JS_LEGACY, + '1.9.0': JS_IR, + '2.0.1': JS_IR, + }, + js: { + '1.3.10': JS_LEGACY, + '1.9.0': JS_IR, + '2.0.1': JS_IR, + }, + wasm: { + '1.3.10': WASM, + '1.9.0': WASM, + '2.0.1': WASM, + }, }); const VERSIONS = [ @@ -44,21 +72,28 @@ test.describe('platform restrictions', () => { }); // offline mode }); - test('JS_IR for unsupported version', async ({ page }) => { - await shouldFailedRun( - page, - 'js-ir', - '1.3.10', - 'JS IR compiler backend accessible only since 1.5.0 version', + test('JS with legacy compiler frontend', async ({ page }) => { + // kotlin.js + jquery.min.js + const unRoute = await mockJsLegacyAssets(page); + await shouldSuccessRun(page, 'js', '1.3.10'); + await checkJsIrWarning(page, () => + shouldSuccessRun(page, 'js-ir', '1.3.10'), ); + await unRoute(); }); - test('JS_IR for supported by minor version', async ({ page }) => { + test('JS with modern compiler frontend', async ({ page }) => { await shouldSuccessRun(page, 'js-ir', '1.9.0'); + await checkJsIrWarning(page, () => + shouldSuccessRun(page, 'js-ir', '1.9.0'), + ); }); - test('JS_IR for supported by major version', async ({ page }) => { - await shouldSuccessRun(page, 'js-ir', '2.0.1'); + test('JS with major version', async ({ page }) => { + await shouldSuccessRun(page, 'js', '2.0.1'); + await checkJsIrWarning(page, () => + shouldSuccessRun(page, 'js-ir', '2.0.1'), + ); }); test('WASM for unsupported version', async ({ page }) => { @@ -90,7 +125,7 @@ test.describe('platform restrictions', () => { async function shouldSuccessRun( page: Page, platform: keyof typeof OUTPUTS, - version: string, + version: keyof (typeof OUTPUTS)[typeof platform], ) { await gotoHtmlWidget( page, @@ -109,7 +144,7 @@ async function shouldSuccessRun( await Promise.all([waitRunRequest(page), runButton(editor)]); resolveRun({ - json: Object.freeze(OUTPUTS[platform]), + json: Object.freeze(OUTPUTS[platform][version]), }); // playground loaded @@ -141,3 +176,22 @@ async function shouldFailedRun( editor.locator(RESULT_SELECTOR).locator('.test-fail'), ).toContainText(text); } + +async function checkJsIrWarning(page: Page, cb: () => Promise) { + let isWarning = false; + + function onConsole(msg: ConsoleMessage) { + if ( + msg.type() === 'warning' && + msg.text() === 'JS_IR is deprecated, use JS + compiler-version instead' + ) + isWarning = true; + } + + page.on('console', onConsole); + + await cb(); + + expect(isWarning).toBe(true); + page.off('console', onConsole); +} diff --git a/tests/utils/mocks/compiler.ts b/tests/utils/mocks/compiler.ts index 04633b03..e353acb0 100644 --- a/tests/utils/mocks/compiler.ts +++ b/tests/utils/mocks/compiler.ts @@ -2,7 +2,8 @@ import { BrowserContext, Route, Request, Page } from '@playwright/test'; import { join } from 'path'; import { RouteFulfill } from '../index'; -export const API_HOST = 'api.kotlinlang.org'; +export const API_HOST = 'api.kotlinlang.org' as const; +export const CDN_HOST = 'cdn.jsdelivr.net' as const; function defaultVersions(route: Route, req: Request) { if (req.method() !== 'GET') { @@ -65,3 +66,36 @@ export async function waitRunRequest(page: Page) { (req) => req.method() === 'POST' && isRunRequest(req.url()), ); } + +function kotlinJSAsset(route: Route, req: Request) { + if (req.method() !== 'GET') { + return route.continue(); + } + + return route.fulfill({ path: join(__dirname, 'kotlin.js') }); +} + +function jqAsset(route: Route, req: Request) { + if (req.method() !== 'GET') { + return route.continue(); + } + + return route.fulfill({ path: join(__dirname, 'jquery.min.js') }); +} + +export async function mockJsLegacyAssets(page: Page | BrowserContext) { + const checkKotlinUrl = (url: URL) => + url.host === CDN_HOST && + url.pathname.match(/^\/?npm\/kotlin@\d+.\d+.\d+\/kotlin\.js$/) !== null; + + const checkJQUrl = (url: URL) => + url.host === CDN_HOST && + url.pathname.match(/^\/?npm\/jquery@1\/dist\/jquery\.min\.js$/) !== null; + + await Promise.all([ + page.route(checkKotlinUrl, kotlinJSAsset), + page.route(checkJQUrl, jqAsset), + ]); + + return () => page.unroute(checkKotlinUrl, kotlinJSAsset); +} diff --git a/tests/utils/mocks/jquery.min.js b/tests/utils/mocks/jquery.min.js new file mode 100644 index 00000000..e8364758 --- /dev/null +++ b/tests/utils/mocks/jquery.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("