diff --git a/Images/LogoTetris.jpg b/Images/LogoTetris.jpg new file mode 100644 index 0000000..99d04f4 Binary files /dev/null and b/Images/LogoTetris.jpg differ diff --git a/Images/button_game_1.png b/Images/button_game_1.png new file mode 100644 index 0000000..156f882 Binary files /dev/null and b/Images/button_game_1.png differ diff --git a/Images/button_game_2.png b/Images/button_game_2.png new file mode 100644 index 0000000..efaaa75 Binary files /dev/null and b/Images/button_game_2.png differ diff --git a/Sources/CustomControls/README.md b/Sources/CustomControls/README.md index 89f5af8..af3334a 100644 --- a/Sources/CustomControls/README.md +++ b/Sources/CustomControls/README.md @@ -48,7 +48,7 @@ This guide provides instructions on how to install Custom Web Controls for a TIA ## Package | | COMMENT | | ------ | ------ | -|0F9F7D83-89C4-469B-A897-40CCE1091384| Template | +|0F9F7D83-89C4-469B-A897-40CCE1091384| Template (pending update) | |9DE938AC-F10E-4DED-B46D-5B3B0618B180| Table | |4E4FE6DA-82F7-4B2A-9C67-4432F6BEE3DE| Tetris | diff --git a/Sources/CustomControls/Table/manifest.json b/Sources/CustomControls/Table/manifest.json index bfd7f03..94b961d 100644 --- a/Sources/CustomControls/Table/manifest.json +++ b/Sources/CustomControls/Table/manifest.json @@ -3,9 +3,9 @@ "mver": "1.2.0", "control": { "identity": { - "name": "TableDnomaid", + "name": "DnomaidTable", "version": "1.0", - "displayname": "Table Dnomaid", + "displayname": "Dnomaid Table", "icon": "./assets/TableDnomaid.ico", "type": "guid://9DE938AC-F10E-4DED-B46D-5B3B0618B180", "start": "./control/index.html" diff --git a/Sources/CustomControls/Table/{9DE938AC-F10E-4DED-B46D-5B3B0618B180}.zip b/Sources/CustomControls/Table/{9DE938AC-F10E-4DED-B46D-5B3B0618B180}.zip index dfd3512..a36f709 100644 Binary files a/Sources/CustomControls/Table/{9DE938AC-F10E-4DED-B46D-5B3B0618B180}.zip and b/Sources/CustomControls/Table/{9DE938AC-F10E-4DED-B46D-5B3B0618B180}.zip differ diff --git a/Sources/CustomControls/Tetris/CWC_manifest_Schema.json b/Sources/CustomControls/Tetris/CWC_manifest_Schema.json new file mode 100644 index 0000000..a0a166f --- /dev/null +++ b/Sources/CustomControls/Tetris/CWC_manifest_Schema.json @@ -0,0 +1,504 @@ +{ + "type": "object", + "required": [ + "mver", + "control" + ], + "properties": { + "mver": { + "type": "string", + "title": "Defines the internal version of the manifest structure, not the version of the CWC itself", + "examples": [ + "1.2.0" + ] + }, + "$schema": { + "type": "string", + "title": "Defines the schema file", + "examples": [ + "./CWC_manifest_Schema.json" + ] + }, + "control": { + "type": "object", + "title": "Defines the kind of manifest", + "required": [ + "identity", + "contracts", + "types" + ], + "properties": { + "identity": { + "type": "object", + "required": [ + "name", + "version", + "displayname", + "type", + "start" + ], + "properties": { + "name": { + "type": "string", + "default": "", + "title": "Defines the name of the CWC as default name when placing this CWC in a screen", + "examples": [ + "LTW Control" + ] + }, + "version": { + "type": "string", + "default": "", + "title": "Defines the version of the CWC", + "examples": [ + "1.0" + ] + }, + "displayname": { + "type": "string", + "default": "", + "title": "Defines the name shown in TIA Portal", + "examples": [ + "LTW Control" + ] + }, + "icon": { + "type": "string", + "default": "", + "title": "Contains the path to the icon of the CWC", + "examples": [ + "./assets/ltw.png" + ] + }, + "type": { + "type": "string", + "default": "", + "title": "Contains the GUID. This needs to be the same as the name of the .zip file.", + "examples": [ + "guid://051efb1b-08cd-4a41-96bd-cd507cd9d2f0" + ] + }, + "start": { + "type": "string", + "default": "", + "title": "defines the starting point of the CWC for the browser", + "examples": [ + "./control/index.html" + ] + }, + "config": { + "type": "string", + "default": "", + "title": "defines the starting point of the Dialogue of the CWC (HMI extension 'Dialogues' required)", + "examples": [ + "./control/configurator.html" + ] + } + }, + "additionalProperties": false + }, + "contracts": { + "type": "object", + "title": "contains methods, events and properties as an interface for the usage in TIA Portal", + "required": [ + "api" + ], + "properties": { + "api": { + "type": "object", + "required": [ + "methods", + "events", + "properties" + ], + "properties": { + "methods": { + "type": "object", + "default": "", + "title": "contains a list of methods the CWC can use.", + "patternProperties": { + "^.*$": { + "type": "object", + "properties": { + "parameters": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "default": "", + "title": "contains a list of parameters of the event.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "default": "", + "enum": [ + "number", + "string", + "boolean", + "object" + ], + "title": "Provides a type for the item", + "examples": [ + "string" + ] + } + } + } + }, + "additionalProperties": false + }, + "description": { + "type": "string", + "default": "", + "title": "Provides a description for the method", + "examples": [ + "downloads the content to the user in the given fileName" + ] + }, + "return": { + "type": "object", + "default": "", + "title": "Defines the return type of the method", + "properties": { + "type": { + "type": "string", + "default": "", + "enum": [ + "number", + "string", + "boolean", + "object", + "null" + ], + "title": "Provides a type for the item", + "examples": [ + "string" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "events": { + "type": "object", + "default": "", + "title": "contains a list of events the CWC can work with.", + "patternProperties": { + "^.*$": { + "type": "object", + "properties": { + "arguments": { + "type": "object", + "patternProperties": { + "^.*$": { + "type": "object", + "default": "", + "title": "contains a list of parameters of the event.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "default": "", + "enum": [ + "number", + "string", + "boolean" + ], + "title": "Provides a type for the item", + "examples": [ + "string" + ] + } + } + } + }, + "additionalProperties": false + }, + "description": { + "type": "string", + "default": "", + "title": "Provides a description for the method", + "examples": [ + "get triggered whenever the screen width or height changes" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "properties": { + "type": "object", + "default": "", + "title": "contains a list of properties the CWC can work with.", + "patternProperties": { + "^.*$": { + "type": "object", + "anyOf": [ + { + "required": [ + "type" + ] + }, + { + "required": [ + "$ref" + ] + } + ], + "properties": { + "type": { + "type": "string", + "default": "", + "enum": [ + "number", + "string", + "boolean" + ], + "title": "Provides a type for the item", + "examples": [ + "string" + ] + }, + "$ref": { + "type": "string", + "default": "", + "title": "use the user defined types ", + "examples": [ + "#/control/types/Zones" + ] + }, + "default": { + "type": [ + "string", + "number", + "boolean" + ], + "default": "", + "title": "Defines the default value", + "examples": [ + "0" + ] + }, + "description": { + "type": "string", + "default": "", + "title": "Describes what this property is used for.", + "examples": [ + "get triggered whenever the screen width or height changes" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "environment": { + "type": "object", + "title": "containing ��prerequisites�� and ��extensions��.", + "properties": { + "prerequisites": { + "type": "object", + "default": "", + "title": "some basic properties", + "properties": { + "renderingspace": { + "type": "object", + "properties": { + "minwidth": { + "type": "number" + }, + "maxwidth": { + "type": "number" + }, + "defaultwidth": { + "type": "number" + }, + "minheight": { + "type": "number" + }, + "maxheight": { + "type": "number" + }, + "defaultheight": { + "type": "number" + }, + "units": { + "type": "string", + "enum": [ + "px", + "cm", + "mm", + "in", + "pt" + ] + } + } + } + }, + "additionalProperties": false + }, + "extensions": { + "type": "object", + "default": "", + "title": "Defines extensions and if they are mandatory or not,", + "properties": { + "HMI": { + "type": "object", + "properties": { + "mandatory": { + "type": "boolean", + "title": "if it is mandatoried", + "examples": [ + "true" + ] + }, + "version": { + "type": "string", + "title": "the version of this item", + "examples": [ + "~1.0.0" + ] + } + }, + "additionalProperties": false + } + } + } + }, + "additionalProperties": false + }, + "metadata": { + "type": "object", + "title": "It is possible to add more user defined metadata", + "patternProperties": { + "author": { + "type": "string", + "default": "", + "examples": [ + "Siemens" + ] + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "type": "string", + "default": "", + "examples": [ + "This is a demo project" + ] + }, + "homepage": { + "type": "string", + "default": "", + "examples": [ + "TestPage01" + ] + } + } + }, + "types": { + "type": "object", + "title": "contains local definitions for user defined datatypes, objects and arrays", + "patternProperties": { + "^.*$": { + "type": "object", + "default": "", + "title": "contains a list of parameters of the event.", + "properties": { + "$id": { + "type": "string", + "default": "", + "title": "the user defined types ", + "enum":[ + "http://tia.siemens.com/wincc-unified/types/s/color", + "http://tia.siemens.com/wincc-unified/types/s/datetime", + "http://tia.siemens.com/wincc-unified/types/s/string", + "http://tia.siemens.com/wincc-unified/types/s/bool", + "http://tia.siemens.com/wincc-unified/types/s/real" + ], + "examples": [ + "http://tia.siemens.com/wincc-unified/types/s/color" + ] + }, + "$ref": { + "type": "string", + "default": "", + "title": "use the user defined types ", + "examples": [ + "#/control/types/Zones" + ] + }, + "type": { + "type": "string", + "default": "", + "enum": [ + "number", + "string", + "boolean", + "object", + "array", + "integer" + ], + "title": "Provides a type for the item", + "examples": [ + "string" + ] + }, + "default": { + "type": [ + "string", + "number", + "boolean" + ], + "default": "", + "title": "The default value", + "examples": [ + "Center" + ] + }, + "enum": { + "type": "array", + "items": { + "type": ["string", "number"] + }, + "title": "The choices", + "examples": [ + "Top", + "Center", + "Bottom" + ] + } + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/Sources/CustomControls/Tetris/assets/TetrisDnomaid.ico b/Sources/CustomControls/Tetris/assets/TetrisDnomaid.ico new file mode 100644 index 0000000..61e7b3f Binary files /dev/null and b/Sources/CustomControls/Tetris/assets/TetrisDnomaid.ico differ diff --git a/Sources/CustomControls/Tetris/control/codeDnomaid.js b/Sources/CustomControls/Tetris/control/codeDnomaid.js new file mode 100644 index 0000000..8c2ed35 --- /dev/null +++ b/Sources/CustomControls/Tetris/control/codeDnomaid.js @@ -0,0 +1,44 @@ + let UnifiedPushbutton = CONTRACT.properties; + // Initialize the custom control (without a successful initialization, the CWC will remain empty. Make sure to include the webcc.min.js script!) + // "result" is a boolean defining if the connection was successfull or not. + function init(result) { + if (result) { + webccInterfaceInit(); + } else { + console.log('Connection NOK'); + } + } + WebCC.start(init, webccInterface.contract, EXTENSIONS, TIMEOUT); + function move(key) { + switch (key) { + case "Rotate": + UnifiedPushbutton[key] = WebCC.Properties[key]; + break; + case "Left": + UnifiedPushbutton[key] = WebCC.Properties[key]; + break; + case "Right": + UnifiedPushbutton[key] = WebCC.Properties[key]; + break; + case "Drop": + UnifiedPushbutton[key] = WebCC.Properties[key]; + break; + } + } + function tetris() { + var script = document.createElement('script'); + script.src = 'tetris.js'; + document.head.appendChild(script); + console.log('Connection OK'); + } + function startGame() { + start(); + } + function pauseGame() { + pause(); + } + function restartGame() { + restart(); + } + + diff --git a/Sources/CustomControls/Tetris/control/dist/style.css b/Sources/CustomControls/Tetris/control/dist/style.css new file mode 100644 index 0000000..c5e4756 --- /dev/null +++ b/Sources/CustomControls/Tetris/control/dist/style.css @@ -0,0 +1,61 @@ +html, +body { + margin: 0 !important; + padding: 0 !important; + width: 100% !important; + height: 100% !important; + color: #51555C; + font-family: "Montserrat", sans-serif; + overflow: hidden; +} + +.clickable { + cursor: pointer; +} + +.dsp-none { + display: none !important; +} + +.dsp-block { + display: block !important; +} + +.margin0 { + margin: 0; +} + +.padding0 { + padding: 0; +} + +#content, +#tableSimple_wrapper { + width: 100%; + height: 100%; +} + +tr { + width: 100%; + margin-right: 0px !important; + margin-left: 0px !important; +} + +th { + padding: 12px 0 12px 4px; +} + +td { + padding: 6px 4px; +} + +tr.selected { + background-color: #A5B4BB !important; + color: #4F535A !important; +} + +tr.selected>.sorting_1, +tr.selected>.sorting_2, +tr.selected>.sorting_3 { + background-color: transparent !important; +} \ No newline at end of file diff --git a/Sources/CustomControls/Tetris/control/dist/styleTetris.css b/Sources/CustomControls/Tetris/control/dist/styleTetris.css new file mode 100644 index 0000000..e2f0270 --- /dev/null +++ b/Sources/CustomControls/Tetris/control/dist/styleTetris.css @@ -0,0 +1,15 @@ +html, body { + height: 100%; + margin: 0; +} + +body { + background: black; + display: flex; + align-items: center; + justify-content: center; +} + +canvas { + border: 1px solid white; +} \ No newline at end of file diff --git a/Sources/CustomControls/Tetris/control/index.html b/Sources/CustomControls/Tetris/control/index.html new file mode 100644 index 0000000..75cb59d --- /dev/null +++ b/Sources/CustomControls/Tetris/control/index.html @@ -0,0 +1,21 @@ + + + +
+ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sources/CustomControls/Tetris/control/tetris.js b/Sources/CustomControls/Tetris/control/tetris.js new file mode 100644 index 0000000..7942427 --- /dev/null +++ b/Sources/CustomControls/Tetris/control/tetris.js @@ -0,0 +1,308 @@ +// https://tetris.fandom.com/wiki/Tetris_Guideline + +// get a random integer between the range of [min,max] +// @see https://stackoverflow.com/a/1527820/2124254 +function getRandomInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +// generate a new tetromino sequence +// @see https://tetris.fandom.com/wiki/Random_Generator +function generateSequence() { + const sequence = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']; + + while (sequence.length) { + const rand = getRandomInt(0, sequence.length - 1); + const name = sequence.splice(rand, 1)[0]; + tetrominoSequence.push(name); + } +} + +// get the next tetromino in the sequence +function getNextTetromino() { + if (tetrominoSequence.length === 0) { + generateSequence(); + } + + const name = tetrominoSequence.pop(); + const matrix = tetrominos[name]; + + // I and O start centered, all others start in left-middle + const col = playfield[0].length / 2 - Math.ceil(matrix[0].length / 2); + + // I starts on row 21 (-1), all others start on row 22 (-2) + const row = name === 'I' ? -1 : -2; + + return { + name: name, // name of the piece (L, O, etc.) + matrix: matrix, // the current rotation matrix + row: row, // current row (starts offscreen) + col: col // current col + }; +} + +// rotate an NxN matrix 90deg +// @see https://codereview.stackexchange.com/a/186834 +function rotate(matrix) { + const N = matrix.length - 1; + const result = matrix.map((row, i) => + row.map((val, j) => matrix[N - j][i]) + ); + + return result; +} + +// check to see if the new matrix/row/col is valid +function isValidMove(matrix, cellRow, cellCol) { + for (let row = 0; row < matrix.length; row++) { + for (let col = 0; col < matrix[row].length; col++) { + if (matrix[row][col] && ( + // outside the game bounds + cellCol + col < 0 || + cellCol + col >= playfield[0].length || + cellRow + row >= playfield.length || + // collides with another piece + playfield[cellRow + row][cellCol + col]) + ) { + return false; + } + } + } + + return true; +} + +// place the tetromino on the playfield +function placeTetromino() { + for (let row = 0; row < tetromino.matrix.length; row++) { + for (let col = 0; col < tetromino.matrix[row].length; col++) { + if (tetromino.matrix[row][col]) { + + // game over if piece has any part offscreen + if (tetromino.row + row < 0) { + return showGameOver(); + } + + playfield[tetromino.row + row][tetromino.col + col] = tetromino.name; + } + } + } + + // check for line clears starting from the bottom and working our way up + for (let row = playfield.length - 1; row >= 0; ) { + if (playfield[row].every(cell => !!cell)) { + + // drop every row above this one + for (let r = row; r >= 0; r--) { + for (let c = 0; c < playfield[r].length; c++) { + playfield[r][c] = playfield[r-1][c]; + } + } + } + else { + row--; + } + } + + tetromino = getNextTetromino(); +} + +// show the game over screen +function showGameOver() { + cancelAnimationFrame(rAF); + gameOver = true; + + context.fillStyle = 'black'; + context.globalAlpha = 0.75; + context.fillRect(0, canvas.height / 2 - 30, canvas.width, 60); + + context.globalAlpha = 1; + context.fillStyle = 'white'; + context.font = '36px monospace'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText('GAME OVER!', canvas.width / 2, canvas.height / 2); +} + +const canvas = document.getElementById('game'); +const context = canvas.getContext('2d'); +const grid = 32; +const tetrominoSequence = []; + +// keep track of what is in every cell of the game using a 2d array +// tetris playfield is 10x20, with a few rows offscreen +const playfield = []; + +// populate the empty state +for (let row = -2; row < 20; row++) { + playfield[row] = []; + + for (let col = 0; col < 10; col++) { + playfield[row][col] = 0; + } +} + +// how to draw each tetromino +// @see https://tetris.fandom.com/wiki/SRS +const tetrominos = { + 'I': [ + [0,0,0,0], + [1,1,1,1], + [0,0,0,0], + [0,0,0,0] + ], + 'J': [ + [1,0,0], + [1,1,1], + [0,0,0], + ], + 'L': [ + [0,0,1], + [1,1,1], + [0,0,0], + ], + 'O': [ + [1,1], + [1,1], + ], + 'S': [ + [0,1,1], + [1,1,0], + [0,0,0], + ], + 'Z': [ + [1,1,0], + [0,1,1], + [0,0,0], + ], + 'T': [ + [0,1,0], + [1,1,1], + [0,0,0], + ] +}; + +// color of each tetromino +const colors = { + 'I': 'cyan', + 'O': 'yellow', + 'T': 'purple', + 'S': 'green', + 'Z': 'red', + 'J': 'blue', + 'L': 'orange' +}; + +let count = 0; +let tetromino = getNextTetromino(); +let rAF = null; // keep track of the animation frame so we can cancel it +let gameOver = false; + +// game loop +function loop() { + rAF = requestAnimationFrame(loop); + context.clearRect(0,0,canvas.width,canvas.height); + + // draw the playfield + for (let row = 0; row < 20; row++) { + for (let col = 0; col < 10; col++) { + if (playfield[row][col]) { + const name = playfield[row][col]; + context.fillStyle = colors[name]; + + // drawing 1 px smaller than the grid creates a grid effect + context.fillRect(col * grid, row * grid, grid-1, grid-1); + } + } + } + + // draw the active tetromino + if (tetromino) { + + // tetromino falls every 35 frames + if (++count > 35) { + tetromino.row++; + count = 0; + + // place piece if it runs into anything + if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) { + tetromino.row--; + placeTetromino(); + } + } + + context.fillStyle = colors[tetromino.name]; + + for (let row = 0; row < tetromino.matrix.length; row++) { + for (let col = 0; col < tetromino.matrix[row].length; col++) { + if (tetromino.matrix[row][col]) { + + // drawing 1 px smaller than the grid creates a grid effect + context.fillRect((tetromino.col + col) * grid, (tetromino.row + row) * grid, grid-1, grid-1); + } + } + } + } + +// listen to keyboard events to move the active tetromino + if (!gameOver){ + if (UnifiedPushbutton.Rotate) { + const matrix = rotate(tetromino.matrix); + if (isValidMove(matrix, tetromino.row, tetromino.col)) { + tetromino.matrix = matrix; + } + UnifiedPushbutton.Rotate = false; + } + if (UnifiedPushbutton.Left) { + const col = tetromino.col - 1; + if (isValidMove(tetromino.matrix, tetromino.row, col)) { + tetromino.col = col; + } + UnifiedPushbutton.Left = false; + } + if (UnifiedPushbutton.Right) { + const col = tetromino.col + 1; + if (isValidMove(tetromino.matrix, tetromino.row, col)) { + tetromino.col = col; + } + UnifiedPushbutton.Right = false; + } + if(UnifiedPushbutton.Drop) { + const row = tetromino.row + 1; + if (!isValidMove(tetromino.matrix, row, tetromino.col)) { + tetromino.row = row - 1; + placeTetromino(); + return; + } + tetromino.row = row; + UnifiedPushbutton.Drop = false; + } + } +} +function start() { + rAF = requestAnimationFrame(loop); +} +function pause() { + cancelAnimationFrame(rAF); +} +function restart() { + cancelAnimationFrame(rAF); + for (let row = -2; row < 20; row++) { + playfield[row] = []; + for (let col = 0; col < 10; col++) { + playfield[row][col] = 0; + } + } + tetrominoSequence.length = 0; + tetromino = getNextTetromino(); + count = 0; + gameOver = false; + context.clearRect(0, 0, canvas.width, canvas.height); + rAF = requestAnimationFrame(loop); +} + + + diff --git a/Sources/CustomControls/Tetris/control/webcc.min.js b/Sources/CustomControls/Tetris/control/webcc.min.js new file mode 100644 index 0000000..4521576 --- /dev/null +++ b/Sources/CustomControls/Tetris/control/webcc.min.js @@ -0,0 +1,2 @@ +/* Version 1.4.2, copyright (C) 2018, Siemens AG. All Rights Reserved. */ +var WebCC=WebCC||function(){var a="pending",b=-1,c=window.parent,d=null,e=null,f={},g=[],h={},i=function(){window.clearTimeout(b),window.removeEventListener?window.removeEventListener("message",d):window.detachEvent("onmessage",d)},j=function(a,b,c){var d=document.createElement("script");d.setAttribute("type","text/javascript"),d.setAttribute("src",a),(b||c)&&(d.addEventListener?(b&&d.addEventListener("load",b),c&&d.addEventListener("error",function(){c("Failed to load "+a+" library")})):b&&(d.onreadystatechange=function(){"loaded"===this.readyState||"complete"===this.readyState?b():c&&c()})),document.getElementsByTagName("head")?document.getElementsByTagName("head")[0].appendChild(d):document.getElementsByTagName("body")[0].appendChild(d)},k=function(a){return JSON.stringify({t:"boot",c:a})},l=function(b,c,d){var l,m,n,o,p,q,r={},s=null,t=null,u=null;if(("waiting"===a||"ok"===a)&&"string"==typeof b.data&&b.data.length>0){try{if(r=JSON.parse(b.data),!(r&&r.t&&r.c))throw new Error("Incompatible message received");if("boot"!==r.t)throw new Error("Unknown message received: "+r.t)}catch(v){return}if(m=function(b){a="failed",i(),d({message:b}),swacPostMessage(k({message:"failed"}),"*")},"boot"===r.t&&"pong"===r.c.message){if(n=function(){SWAC.isContainer=!1,a="ok",e=r.c,e.containerVersion=e.containerVersion||"1.0.0",swacPostMessage(k({message:"ok"}),"*")},p=function(){var a,b=g.length,c=null;if("undefined"!=typeof SWAC.Hub.prototype.Extensions)c=SWAC.Hub.prototype.Extensions;else{if("undefined"==typeof WebCC)return n(),void 0;c=WebCC.Extensions}if(null!==t){if(Object.keys(c).length!==t+1&&"undefined"==typeof SWACBoot)return m("Invalid Extension"),void 0;for(var d in c)c.hasOwnProperty(d)&&"undefined"!=typeof h[d]&&(c[h[d]]=c[d],delete c[d],delete h[d])}0===b||"undefined"==typeof WebCC&&SWAC._internal.Utils.checkVersion(u,"1.4.1")<0?n():(a=g.pop(),"undefined"==typeof defineExtension?j(a,p,m):"undefined"==typeof SWACBoot&&"undefined"==typeof WebCC.Extensions?(s=/^\s+|\s+$/g,void 0===a||null===a||""===a.replace(s,"")?m("Failed to load SWAC.Config.Control.URLs library"):(l=function(){"undefined"!=typeof WebCC.Extensions?p():m("Failed to load SWAC.Config.Control.URLs library")},j(a,l,m)),s=null):"$$unknownExtension$$"===a?"undefined"!=typeof SWACBoot?p():m("Unknown Extension"):(t=Object.keys(c).length,j(a,p,"undefined"!=typeof SWACBoot?p:m)))},r.c.extensions)for(q in r.c.extensions)r.c.extensions.hasOwnProperty(q)&&g.unshift(r.c.extensions[q]);u=r.c.containerVersion||"1.0.0","undefined"!=typeof SWAC?p():(s=/^\s+|\s+$/g,void 0===r.c.url||null===r.c.url||""===r.c.url.replace(s,"")?m("Failed to load SWAC.Config.Container.URLs library"):(l=function(){"undefined"!=typeof SWAC||"undefined"!=typeof r.c.namespace&&"undefined"!=typeof window[r.c.namespace]?p():m("Failed to load SWAC.Config.Container.URLs library")},a="upgrading",j(r.c.url,l,m)),s=null)}else if("boot"===r.t&&"ok2"===r.c.message){a="done",i();for(o in e)e.hasOwnProperty(o)&&(f[o]=e[o]);e.message="SWAC successfully loaded",e.auth=e.authentication,delete e.authentication,delete e.url,delete e.extensions,delete e.namespace,r.c.details||(f.details={path:["