├── .npmignore ├── .gitignore ├── images ├── close.png ├── slice1.png ├── minimize.png ├── restore.png ├── window-manager.afdesign ├── minimize.svg ├── close.svg ├── maximize.svg ├── import.js ├── restore.svg └── resize.svg ├── docs ├── jsdoc │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── scripts │ │ ├── prettify │ │ │ ├── lang-css.js │ │ │ ├── Apache-License-2.0.txt │ │ │ └── prettify.js │ │ └── main.js │ ├── styles │ │ ├── prettify-jsdoc.css │ │ ├── prettify-tomorrow.css │ │ └── main.css │ ├── module-EventEmitter.html │ ├── external-EventEmitter.html │ ├── window-options.js.html │ ├── src_html.js.html │ ├── EventEmitter-.html │ ├── window-manager.js.html │ └── src_windowOptions.js.html ├── index.css ├── index.html ├── rollup.config.js ├── rollup.debug.js └── src │ ├── menu.js │ └── code.js ├── rollup.config.js ├── src ├── html.js ├── windowOptions.js ├── images.js ├── Snap.js └── WindowManager.js ├── LICENSE ├── package.json ├── .jsdoc.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | docs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .parcel-cache 4 | .cache -------------------------------------------------------------------------------- /images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/images/close.png -------------------------------------------------------------------------------- /images/slice1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/images/slice1.png -------------------------------------------------------------------------------- /images/minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/images/minimize.png -------------------------------------------------------------------------------- /images/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/images/restore.png -------------------------------------------------------------------------------- /images/window-manager.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/images/window-manager.afdesign -------------------------------------------------------------------------------- /docs/jsdoc/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/docs/jsdoc/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/docs/jsdoc/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/jsdoc/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/docs/jsdoc/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidfig/window-manager/HEAD/docs/jsdoc/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/index.css: -------------------------------------------------------------------------------- 1 | html 2 | { 3 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 4 | height: 100%; 5 | } 6 | 7 | body 8 | { 9 | margin: 0; 10 | background: #aaaaaa; 11 | } 12 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | simple-window-manager 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import { terser } from 'rollup-plugin-terser' 4 | 5 | export default 6 | { 7 | input: 'docs/src/code.js', 8 | plugins: [ 9 | resolve(), 10 | commonjs(), 11 | terser() 12 | ], 13 | output: 14 | { 15 | file: 'docs/index.js', 16 | format: 'iife', 17 | sourcemap: true 18 | } 19 | } -------------------------------------------------------------------------------- /images/minimize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/rollup.debug.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | import serve from 'rollup-plugin-serve' 4 | import livereload from 'rollup-plugin-livereload' 5 | 6 | export default 7 | { 8 | input: 'docs/src/code.js', 9 | plugins: [ 10 | resolve(), 11 | commonjs(), 12 | serve( 13 | { 14 | contentBase: 'docs', 15 | verbose: true 16 | }), 17 | livereload({ dist: 'docs' }) 18 | ], 19 | output: 20 | { 21 | file: 'docs/index.js', 22 | format: 'iife', 23 | sourcemap: true 24 | } 25 | } -------------------------------------------------------------------------------- /images/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/maximize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/import.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | 4 | async function importFiles() 5 | { 6 | const images = {} 7 | const files = await fs.readdir(path.join(__dirname)) 8 | for (const file of files) 9 | { 10 | if (file.includes('.svg')) 11 | { 12 | images[file.replace('.svg', '')] = await fs.readFile(path.join(__dirname, file)) + '' 13 | } 14 | } 15 | let s = '' 16 | for (const name in images) 17 | { 18 | s += `export const ${name}='${images[name]}';` 19 | } 20 | await fs.outputFile(path.join(__dirname, '..', 'src', 'images.js'), s) 21 | process.exit(0) 22 | } 23 | 24 | importFiles() -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { terser } from 'rollup-plugin-terser' 2 | import resolve from '@rollup/plugin-node-resolve' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | 5 | export default [ 6 | { 7 | input: 'src/WindowManager.js', 8 | plugins: [ 9 | resolve(), 10 | commonjs(), 11 | terser() 12 | ], 13 | output: 14 | { 15 | file: 'public/simple-window-manager.min.js', 16 | format: 'umd', 17 | name: 'WindowManager', 18 | sourcemap: true 19 | } 20 | }, 21 | { 22 | input: 'src/WindowManager.js', 23 | plugins: [ 24 | resolve(), 25 | commonjs() 26 | ], 27 | output: 28 | { 29 | file: 'public/simple-window-manager.es.js', 30 | format: 'esm', 31 | sourcemap: true 32 | } 33 | }] -------------------------------------------------------------------------------- /images/restore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /src/html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * shortcut to create an html element 3 | * @param {object} options 4 | * @param {type} [options.string=div] 5 | * @param {string} [options.className] 6 | * @param {object} [options.styles] 7 | * @param {HTMLElement} [options.parent] 8 | * @param {string} [options.html] 9 | * @returns {HTMLElement} 10 | */ 11 | export function html(options={}) 12 | { 13 | const object = document.createElement(options.type || 'div') 14 | if (options.parent) 15 | { 16 | options.parent.appendChild(object) 17 | } 18 | if (options.styles) 19 | { 20 | Object.assign(object.style, options.styles) 21 | } 22 | if (options.className) 23 | { 24 | object.className = options.className 25 | } 26 | if (options.html) 27 | { 28 | object.innerHTML = options.html 29 | } 30 | return object 31 | } -------------------------------------------------------------------------------- /images/resize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 YOPEY YOPEY LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/src/menu.js: -------------------------------------------------------------------------------- 1 | import Menu from 'yy-menu' 2 | const Item = Menu.MenuItem 3 | 4 | export function menu(wm) 5 | { 6 | const file = new Menu() 7 | file.append(new Item({ label: '&New Window', accelerator: 'ctrl+m', click: () => newWindow(wm) })) 8 | 9 | const windows = new Menu() 10 | for (let i = 0; i < wm.windows.length; i++) 11 | { 12 | windows.append(new Item({ 13 | type: 'checkbox', label: 'Window ' + (i < 10 ? '&' : '') + i, accelerator: i < 10 ? 'ctrl+' + i : null, checked: true, click: (e, item) => 14 | { 15 | if (item.checked) 16 | { 17 | wm.windows[i].open() 18 | } 19 | else 20 | { 21 | wm.windows[i].close() 22 | } 23 | } 24 | })) 25 | } 26 | const about = new Menu() 27 | about.append(new Item({ label: 'simple-&window-manager', click: () => aboutWM()})) 28 | about.append(new Item({ label: 'yy-menu', click: () => aboutMenu() })) 29 | 30 | const main = new Menu() 31 | main.append(new Item({ label: '&File', submenu: file })) 32 | main.append(new Item({ label: '&Windows', submenu: windows })) 33 | main.append(new Item({ label: 'About', submenu: about })) 34 | Menu.setApplicationMenu(main) 35 | } 36 | 37 | function newWindow() 38 | { 39 | 40 | } 41 | 42 | function aboutWM() 43 | { 44 | 45 | } 46 | 47 | function aboutMenu() 48 | { 49 | 50 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-window-manager", 3 | "version": "2.0.5", 4 | "description": "A javascript-only Window Manager", 5 | "main": "public/simple-window-manager.min.js", 6 | "module": "public/simple-window-manager.es.js", 7 | "scripts": { 8 | "start": "rollup -c docs/rollup.debug.js --watch", 9 | "docs": "jsdoc -c .jsdoc.json", 10 | "build-images": "node images/import", 11 | "build-demo": "rollup -c docs/rollup.config.js", 12 | "build": "rollup -c rollup.config.js", 13 | "prepublishOnly": "yarn build-demo && yarn build && yarn docs" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/davidfig/window-manager.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/davidfig/window-manager/issues" 21 | }, 22 | "homepage": "https://github.com/davidfig/window-manager#readme", 23 | "author": "David Figatner (david@yopeyopey.com)", 24 | "license": "MIT", 25 | "dependencies": { 26 | "clicked": "^3.2.3", 27 | "eventemitter3": "^4.0.4" 28 | }, 29 | "devDependencies": { 30 | "@rollup/plugin-commonjs": "^12.0.0", 31 | "@rollup/plugin-node-resolve": "^8.0.0", 32 | "fs-extra": "^9.0.0", 33 | "jsdoc": "^3.6.4", 34 | "rollup": "^2.10.5", 35 | "rollup-plugin-livereload": "^1.3.0", 36 | "rollup-plugin-serve": "^1.0.1", 37 | "rollup-plugin-terser": "^5.3.0", 38 | "taffydb": "^2.7.3", 39 | "underscore": "^1.10.2", 40 | "yy-fps": "^1.1.0", 41 | "yy-jsdoc-template": "^1.3.0", 42 | "yy-menu": "^1.6.1" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/main.js: -------------------------------------------------------------------------------- 1 | (function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return''+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('.item[data-name*="'+n+'"]:eq(0)');if(s.length){s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}}); -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "applicationName": "simple-window-manager", 3 | "disqus": "", 4 | "googleAnalytics": "", 5 | "openGraph": { 6 | "title": "", 7 | "type": "website", 8 | "image": "", 9 | "site_name": "", 10 | "url": "" 11 | }, 12 | "meta": { 13 | "title": "simple-window-manager", 14 | "description": "", 15 | "keyword": "" 16 | }, 17 | "linenums": true, 18 | "source": { 19 | "include": [ 20 | "./src", 21 | "./node_modules/eventemitter3" 22 | ], 23 | "includePattern": ".+\\.js(doc)?$", 24 | "excludePattern": "(^|\\/|\\\\)_" 25 | }, 26 | "opts": { 27 | "readme": "./README.md", 28 | "encoding": "utf8", 29 | "recurse": true, 30 | "private": false, 31 | "lenient": true, 32 | "destination": "./docs/jsdoc", 33 | "template": "../jsdoc-template" 34 | }, 35 | "plugins": [ 36 | "./node_modules/yy-jsdoc-template/plugins/skip-node_modules.js" 37 | ], 38 | "templates": { 39 | "default": { 40 | "outputSourceFiles": true 41 | }, 42 | "applicationName": "simple-window-manager", 43 | "footer" : "by David Figatner (david@yopeyopey.com)", 44 | "copyright" : "Copyright © 2020 YOPEY YOPEY LLC.", 45 | "meta": { 46 | "title": "simple-window-manager API Documentation", 47 | "description": "Documentation for simple-window-manager library", 48 | "keyword": "docs, documentation, html5, javascript, jsdoc, window manager" 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /docs/jsdoc/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/windowOptions.js: -------------------------------------------------------------------------------- 1 | import { close, maximize, restore, resize } from './images' 2 | 3 | /** 4 | * @typedef {object} WindowOptions 5 | * @property {number} [x=0] 6 | * @property {number} [y=0] 7 | * @property {number} [width] 8 | * @property {number} [height] 9 | * @property {boolean} [modal] 10 | * @property {boolean} [openOnCreate=true] 11 | * @property {boolean} [movable=true] 12 | * @property {boolean} [resizable=true] 13 | * @property {boolean} [maximizable=true] 14 | * @property {boolean} [closable=true] 15 | * @property {boolean} [noSnap] don't snap this window or use this window as a snap target 16 | * @property {boolean} [titlebar=true] 17 | * @property {string} [titlebarHeight=36px] 18 | * @property {boolean} [titleCenter] 19 | * @property {string} [minWidth=200px] 20 | * @property {string} [minHeight=60px] 21 | * @property {string} [borderRadius=4px] 22 | * @property {object} [styles] 23 | * @property {string} [shadow='0 0 12px 1px rgba(0, 0, 0, 0.6)'] 24 | * @property {number} [animateTime=250] 25 | * @property {string} [backgroundModal=rgba(0,0,0,0.6)] 26 | * @property {string} [backgroundWindow=#fefefe] 27 | * @property {string} [backgroundTitlebarActive=#365d98] 28 | * @property {string} [backgroundTitlebarInactive=#888888] 29 | * @property {string} [foregroundButton=#ffffff] 30 | * @property {string} [foregroundTitle=#ffffff] 31 | * @property {string} [maximizeButton=...] 32 | * @property {string} [closeButton=...] 33 | * @property {string} [resize=...] 34 | */ 35 | export const windowOptions = { 36 | x: 0, 37 | y: 0, 38 | width: undefined, 39 | height: undefined, 40 | modal: false, 41 | openOnCreate: true, 42 | 43 | classNames: {}, 44 | 45 | minWidth: '200px', 46 | minHeight: '60px', 47 | borderRadius: 0, 48 | styles: {}, 49 | 50 | shadow: 'none', 51 | movable: true, 52 | resizable: true, 53 | maximizable: true, 54 | closable: true, 55 | 56 | titlebar: true, 57 | titlebarHeight: '2rem', 58 | 59 | backgroundModal: 'rgba(0, 0, 0, 0.6)', 60 | backgroundWindow: '#fefefe', 61 | backgroundTitlebarActive: '#365d98', 62 | backgroundTitlebarInactive: '#888888', 63 | foregroundButton: '#ffffff', 64 | foregroundTitle: '#ffffff', 65 | 66 | closeButton: close, 67 | maximizeButton: maximize, 68 | restoreButton: restore, 69 | 70 | backgroundResize: resize 71 | } -------------------------------------------------------------------------------- /docs/jsdoc/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Menlo, Monaco, Consolas, monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-window-manager 2 | A javascript-only Window Manager 3 | 4 | ## version 2 5 | * the API has changed from v1 to v2 6 | * rollup is used to compile the libraries, so there is no longer a default export: see sample code below 7 | * animations are deprecated for now since they felt slow--I'm open to putting them back in 8 | * finally moved away from `style.left` and `style.top` to `transform: translate(x, y)` (should have done this sooner) 9 | * snapping working much better; screen snapping now optionally responds to window resize 10 | * minimize has been removed since it does the same thing as close (unless we add a taskbar) 11 | 12 | ## features 13 | * basic windowing experience (works great with electron to run multiple windows under one process) 14 | * create normal and modal windows 15 | * optionally snap windows to screen edges and/or other windows 16 | * takes advantage of all the features of the DOM, including undefined width and/or height to automatically adjust size of window based on content 17 | * windows may be resized, maximized, and minimized 18 | * minimize works by minimizing to a small square that can be moved independently. Clicking it restores to its original size and location. Minimizing again moves the small square back to the last minimized location. 19 | * can save and load windowing state (e.g., using localStorage or json files using Electron) 20 | * emits events (using eventemitter3) 21 | * uses javascript animations instead of CSS 22 | 23 | ## rationale 24 | 25 | I used [Ventus](https://github.com/rlamana/Ventus) to build internal tools and editors, but I wanted a more configurable solution with a better event model that didn't rely on CSS. 26 | 27 | ## live example 28 | [https://davidfig.github.io/window-manager/](https://davidfig.github.io/window-manager/) 29 | 30 | ## installation 31 | 32 | npm i simple-window-manager 33 | 34 | ## API documentation 35 | [https://davidfig.github.io/window-manager/jsdoc/](https://davidfig.github.io/window-manager/jsdoc/) 36 | 37 | ## sample code 38 | ```js 39 | import { WindowManager } from 'simple-window-manager' 40 | // or const WindowManager = require('simple-window-manager').WindowManager 41 | 42 | // this is the window manager with one of the default options changed 43 | const wm = new WindowManager({ backgroundWindow: 'green' }); 44 | 45 | // enable window snapping to screen edges and other windows when moving 46 | wm.snap() 47 | 48 | // create a window 49 | const window = wm.createWindow({ width: 500, height: 500, title: 'Test Window' }) 50 | 51 | // set content of window 52 | window.content.style.margin = '10px' 53 | window.content.innerHTML = 'This is a nifty window.' 54 | ``` 55 | 56 | ## License 57 | MIT License 58 | (c) 2020 [YOPEY YOPEY LLC](https://yopeyopey.com/) by [David Figatner](david@yopeyopey.com) 59 | -------------------------------------------------------------------------------- /src/images.js: -------------------------------------------------------------------------------- 1 | export const close='';export const maximize='';export const minimize='';export const resize='';export const restore=''; -------------------------------------------------------------------------------- /docs/src/code.js: -------------------------------------------------------------------------------- 1 | import FPS from 'yy-fps' 2 | 3 | import { WindowManager } from '../../src/WindowManager' 4 | import { html } from '../../src/html' 5 | 6 | // import { menu } from './menu' 7 | 8 | // create a window manager and change some of the default styles 9 | const wm = new WindowManager() 10 | wm.snap({ screen: true, windows: true, spacing: 0 }) 11 | 12 | window.onload = () => 13 | { 14 | // creates test windows 15 | test() 16 | test2() 17 | test3() 18 | test4() 19 | test5() 20 | test7() 21 | // menu(wm) 22 | update() 23 | } 24 | 25 | const top = 10 26 | 27 | function test() 28 | { 29 | const test = wm.createWindow({ x: 10, y: top, titlebar: false, title: 'Test Window', resizable: false, maximizable: false, minimizable: false, titleCenter: true, closable: false }) 30 | test.content.style.padding = '1em' 31 | test.content.innerHTML = 'This is a test window.' 32 | test.open() 33 | } 34 | 35 | function test2() 36 | { 37 | const test = wm.createWindow({ 38 | width: 300, height: 150, 39 | x: 100, y: 100, 40 | titlebarHeight: '22px', 41 | backgroundTitlebarActive: 'green', 42 | backgroundTitlebarInactive: 'purple', 43 | backgroundWindow: 'rgb(255,200,255)', 44 | }) 45 | test.content.style.padding = '0.5em' 46 | test.content.innerHTML = 'This is a pink test window.

Check out the fancy title bar for other style tests.


And scrolling!!!' 47 | test.open() 48 | } 49 | 50 | function test3() 51 | { 52 | // create a test window with a button to create a modal window 53 | const test = wm.createWindow({ x: 300, y: 400, width: 350, title: 'This is one fancy demo!' }) 54 | test.content.style.padding = '1em' 55 | html({ parent: test.content, html: 'OK. It isn\'t that fancy, but it shows off some of the functionality of this library.

Please excuse the mess. I do NOT keep my desktop this messy, but I thought it made for a good demo.' }) 56 | const div = html({ parent: test.content, styles: { textAlign: 'center', marginTop: '1em' } }) 57 | const button = html({ parent: div, type: 'button', html: 'open modal window' }) 58 | 59 | let x = 0 60 | let y = 0 61 | function createModal() 62 | { 63 | // create a modal window 64 | const modal = wm.createWindow({ 65 | modal: true, 66 | width: 200, 67 | title: 'modal window', 68 | minimizable: false, 69 | maximizable: false 70 | }) 71 | const div = html({ parent: modal.content, styles: { 'margin': '0.5em' } }) 72 | html({ parent: div, html: 'This needs to be closed before using other windows.' }) 73 | const buttonDiv = html({ parent: div, styles: { 'text-align': 'center', margin: '1em', display: 'flex' } }) 74 | const buttonAdd = html({ parent: buttonDiv, type: 'button', html: 'open child modal' }) 75 | const button = html({ parent: buttonDiv, type: 'button', html: 'close modal' }) 76 | button.onclick = () => modal.close() 77 | buttonAdd.onclick = () => createModal() 78 | modal.open() 79 | 80 | // center window in test 81 | modal.center(test) 82 | modal.move(modal.x + x, modal.y + y) 83 | x += 20 84 | y += 20 85 | } 86 | 87 | button.onclick = () => createModal(div) 88 | test.open() 89 | } 90 | 91 | function test4() 92 | { 93 | const test = wm.createWindow({ x: 300, y: top, title: 'My wife\'s art gallery!', maximizable: false }) 94 | test.content.innerHTML = '' 95 | test.open() 96 | test.sendToBack() 97 | } 98 | 99 | function test5() 100 | { 101 | const test = wm.createWindow({ x: 20, y: 600, title: 'window save/load' }) 102 | html({ parent: test.content, html: 'Save the windows, and then move windows around and load them.', styles: { margin: '0.5em' } }) 103 | const buttons = html({ parent: test.content, styles: { 'text-align': 'center' } }) 104 | const save = html({ parent: buttons, html: 'save window state', type: 'button', styles: { margin: '1em', background: 'rgb(200,255,200)' } }) 105 | const load = html({ parent: buttons, html: 'load window state', type: 'button', styles: { margin: '1em', background: 'rgb(255,200,200)' } }) 106 | test.open() 107 | let data 108 | save.onclick = () => data = wm.save() 109 | load.onclick = () => { if (data) wm.load(data) } 110 | } 111 | 112 | function test7() 113 | { 114 | const test = wm.createWindow({ x: 700, y: 40, width: 400, height: 300, title: 'API documentation', styles: { overflow: 'hidden' } }) 115 | test.content.innerHTML = '' 116 | test.open() 117 | } 118 | 119 | const wallpaper = html({ parent: wm.overlay, styles: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', color: 'white' } }) 120 | wallpaper.innerHTML = 'You can also use the background as wallpaper or another window surface.' 121 | 122 | const fps = new FPS() 123 | function update() 124 | { 125 | fps.frame(false, true) 126 | requestAnimationFrame(update) 127 | } -------------------------------------------------------------------------------- /docs/jsdoc/styles/main.css: -------------------------------------------------------------------------------- 1 | body,html{font-family:helvetica,arial,sans-serif;background-color:#fff;color:#333;font-size:1em}ol,ul{margin:0;padding:0}li{list-style-type:none}#wrap{position:relative}.list::-webkit-scrollbar{width:8px;background-color:transparent}.list::-webkit-scrollbar-thumb{background-color:#647086;border-radius:4px}.navigation{position:fixed;overflow:hidden;min-width:250px;width:25%;top:0;left:0;bottom:0;background-color:#222}.navigation .menu-toggle{display:none}@media screen and (max-width:768px){.navigation{left:0;position:relative;width:100%;overflow:auto}.navigation .list,.navigation .search{display:none}.navigation .list.show,.navigation .search.show{display:block;position:static}.navigation .menu-toggle{display:block;position:absolute;top:10px;right:10px}}.navigation .applicationName{margin:0;padding:20px;font-weight:700;white-space:nowrap;color:#fff}.navigation .applicationName a{color:#fff}.navigation .search{padding:0 20px}.navigation .search input{background-color:#14171d;color:#fff;border-color:#3d495a}.navigation .list{padding:20px;position:absolute;overflow:auto;-webkit-overflow-scrolling:touch;width:100%;top:100px;bottom:0}.navigation li.item{margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid #3d495a;overflow:hidden}.navigation li.item a{color:#647086}.navigation li.item a:hover{color:#fff}.navigation li.item .title{display:block}.navigation li.item .title a{display:block;color:#cfd4db}.navigation li.item .title a:hover{color:#fff}.navigation li.item .title.namespace a{color:#fff}.navigation li.item .title .namespaceTag{display:inline-block;border-radius:3px;background-color:#ffd900;color:#fff;font-size:70%;padding:2px 6px;float:left;margin-right:10px;pointer-events:none}.navigation li.item .subtitle{margin:10px 0;font-weight:700;color:#ffd900;display:block;letter-spacing:.05em}.navigation li.item ul>li{padding-left:10px;font-size:.9em}.navigation li.item .itemMembers li.parent a{color:#a9b3c3}.navigation .item,.navigation .itemMembers,.navigation .itemMembers li{display:none}.navigation.not-searching .item{display:block}.navigation.not-searching .item.current .itemMembers,.navigation.not-searching .item.current .itemMembers li{display:block}.navigation.searching .item.match{display:block}.navigation.searching .item.match .itemMembers li.match,.navigation.searching .item.match .itemMembers.match{display:block}.content-size{max-width:1000px;min-width:300px;margin-left:auto;margin-right:auto}.status-deprecated{text-decoration:line-through;opacity:.4}.status-deprecated a,.status-deprecated:hover{text-decoration:none}.main{left:25%;position:fixed;height:100%;right:0;overflow:auto;-webkit-overflow-scrolling:touch;word-break:break-word}.main .summary-list dt{width:100%;margin-bottom:4px;float:left;font-size:110%}@media screen and (min-width:768px){.main .summary-list dt{width:50%}}@media screen and (min-width:991px){.main .summary-list dt{width:33.33%}}@media screen and (min-width:1200px){.main .summary-list dt{width:25%}}@media screen and (max-width:1000px){.main{left:250px}}@media screen and (max-width:768px){.main{left:0;position:static}}.main img{max-width:100%}.main article{padding:20px}.main header{background:#fff}.main header .class-description{font-size:120%}.main header .header{padding:20px}.main header .header h2{font-weight:700}.main .page-title{display:none}.main .access-signature{font-weight:400;display:inline-block;border-radius:3px;background-color:#79859a;color:#fff;font-size:.7em;padding:2px 6px;margin-left:6px}.main .access-signature.deprecated{background-color:#e91e63;font-weight:700}.main .access-signature.deprecated .deprecated-info{font-weight:400;margin-left:5px}.main .access-signature a{color:#fff}.main h4.name .type-signature{font-weight:400;font-size:.8em;color:#79859a}.main h4.name .type-signature:before{content:' : ';opacity:.6}.main h4.name .return-symbol{margin:0 6px;color:#79859a;font-size:80%}.main h4.name .share-icon{font-size:70%;color:#79859a}.main .subsection-title{color:#e91e63}.main .description{margin-top:10px}.main .description ol,.main .description ul{margin-bottom:15px}.main .description h2{font-weight:700;margin-top:30px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #efefef}.main .description pre{margin:10px 0}.main .tag-source{font-size:85%}.main dt.tag-source{margin-top:5px}.main dt.tag-default{color:#79859a}.main .nameContainer{position:relative}.main .nameContainer .tag-source{position:absolute;top:0;right:0;padding:2px 6px;border-bottom-left-radius:4px;border-bottom-right-radius:4px;background-color:#b3b7c3}.main .nameContainer .tag-source a{color:#fff;font-weight:400}.main .nameContainer h4{font-weight:700;padding:20px 0 0;border-top:1px solid #c8c9cc}.main .nameContainer h4 .signature{font-weight:400;font-size:.8em;padding-left:.4em}.main table{width:100%;margin-bottom:15px;margin-top:5px}.main table th{padding:3px 3px;color:#fff;font-weight:400;background:#79859a}.main table td{vertical-align:top;padding:5px 3px;word-break:normal}.main table tbody tr:nth-child(odd){background-color:#fff}.main table tbody tr:nth-child(even){background-color:#f5f5fb}.main table .type{color:#79859a}.main table .attributes{color:#79859a}.main table .description p{margin:0}.main table .optional{float:left;border-radius:3px;background-color:#b3b7c3;padding:2px 4px;margin-right:5px;color:#fff;font-size:80%}.main .readme p{margin-top:15px}.main .readme h1{font-weight:700}.main .readme h2{font-weight:700;margin-top:30px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #79859a}.main .readme h3{color:#e91e63}.main .readme li{margin-bottom:10px}.main article ol,.main article ul{margin-left:25px}.main article ol>li{list-style-type:decimal;margin-bottom:5px}.main article ul>li{margin-bottom:5px;list-style-type:disc}.footer{margin:0 20px 20px;padding-top:20px;text-align:right;font-size:.9em;color:#79859a;border-top:1px solid #c8c9cc} -------------------------------------------------------------------------------- /src/Snap.js: -------------------------------------------------------------------------------- 1 | import { html } from './html' 2 | 3 | const DEFAULT_COLOR = '#a8f0f4' 4 | const DEFAULT_SIZE = 10 5 | 6 | const SnapOptionsDefault = { 7 | screen: true, 8 | windows: true, 9 | snap: 20, 10 | color: DEFAULT_COLOR, 11 | spacing: 5, 12 | indicator: DEFAULT_SIZE 13 | } 14 | 15 | export class Snap 16 | { 17 | /** 18 | * add edge snapping plugin 19 | * @param {WindowManager} wm 20 | * @param {object} [options] 21 | * @param {boolean} [options.screen=true] snap to screen edges 22 | * @param {boolean} [options.windows=true] snap to window edges 23 | * @param {number} [options.snap=20] distance to edge in pixels before snapping and width/height of snap bars 24 | * @param {string} [options.color=#a8f0f4] color for snap bars 25 | * @param {number} [options.spacing=5] spacing distance between window and edges 26 | * @param {number} [options.indicator=10] size in pixels of snapping indicator (the indicator is actually twice the size of what is shown) 27 | */ 28 | constructor(wm, options={}) 29 | { 30 | this.wm = wm 31 | this.options = Object.assign({}, SnapOptionsDefault, options) 32 | this.highlights = html({ parent: this.wm.overlay, styles: { 'position': 'absolute' } }) 33 | this.horizontal = html({ 34 | parent: this.highlights, styles: { 35 | display: 'none', 36 | position: 'absolute', 37 | height: `${this.options.indicator}px`, 38 | borderRadius: `${this.options.indicator}px`, 39 | backgroundColor: this.options.color 40 | } 41 | }) 42 | this.vertical = html({ 43 | parent: this.highlights, styles: { 44 | display: 'none', 45 | position: 'absolute', 46 | width: `${this.options.indicator}px`, 47 | borderRadius: `${this.options.indicator}px`, 48 | backgroundColor: this.options.color 49 | } 50 | }) 51 | this.horizontal 52 | this.showing = [] 53 | } 54 | 55 | stop() 56 | { 57 | this.highlights.remove() 58 | this.stopped = true 59 | } 60 | 61 | addWindow(win) 62 | { 63 | win.on('move', () => this.move(win)) 64 | win.on('move-end', () => this.moveEnd(win)) 65 | } 66 | 67 | screenMove(rect, horizontal, vertical) 68 | { 69 | const width = document.body.clientWidth 70 | const height = document.body.clientHeight 71 | if (rect.left - this.options.snap <= width && rect.right + this.options.snap >= 0) 72 | { 73 | if (Math.abs(rect.top - 0) <= this.options.snap) 74 | { 75 | horizontal.push({ distance: Math.abs(rect.top - 0), left: 0, width, top: 0, side: 'top', screen: true }) 76 | } 77 | else if (Math.abs(rect.bottom - height) <= this.options.snap) 78 | { 79 | horizontal.push({ distance: Math.abs(rect.bottom - height), left: 0, width, top: height, side: 'bottom', screen: true }) 80 | } 81 | } 82 | if (rect.top - this.options.snap <= height && rect.bottom + this.options.snap >= 0) 83 | { 84 | if (Math.abs(rect.left - 0) <= this.options.snap) 85 | { 86 | vertical.push({ distance: Math.abs(rect.left - 0), top: 0, height, left: 0, side: 'left', screen: true }) 87 | } 88 | else if (Math.abs(rect.right - width) <= this.options.snap) 89 | { 90 | vertical.push({ distance: Math.abs(rect.right - width), top: 0, height, left: width, side: 'right', screen: true }) 91 | } 92 | } 93 | } 94 | 95 | windowsMove(original, rect, horizontal, vertical) 96 | { 97 | for (let win of this.wm.windows) 98 | { 99 | if (!win.options.noSnap && win !== original) 100 | { 101 | const rect2 = win.win.getBoundingClientRect() 102 | if (rect.left - this.options.snap <= rect2.right && rect.right + this.options.snap >= rect2.left) 103 | { 104 | if (Math.abs(rect.top - rect2.bottom) <= this.options.snap) 105 | { 106 | horizontal.push({ distance: Math.abs(rect.top - rect2.bottom), left: rect2.left, width: rect2.width, top: rect2.bottom, side: 'top' }) 107 | if (Math.abs(rect.left - rect2.left) <= this.options.snap) 108 | { 109 | vertical.push({ distance: Math.abs(rect.left - rect2.left), top: rect2.top, height: rect2.height, left: rect2.left, side: 'left', noSpacing: true }) 110 | } 111 | else if (Math.abs(rect.right - rect2.right) <= this.options.snap) 112 | { 113 | vertical.push({ distance: Math.abs(rect.right - rect2.right), top: rect2.top, height: rect2.height, left: rect2.right, side: 'right', noSpacing: true }) 114 | } 115 | } 116 | else if (Math.abs(rect.bottom - rect2.top) <= this.options.snap) 117 | { 118 | horizontal.push({ distance: Math.abs(rect.bottom - rect2.top), left: rect2.left, width: rect2.width, top: rect2.top, side: 'bottom' }) 119 | if (Math.abs(rect.left - rect2.left) <= this.options.snap) 120 | { 121 | vertical.push({ distance: Math.abs(rect.left - rect2.left), top: rect2.top, height: rect2.height, left: rect2.left, side: 'left', noSpacing: true }) 122 | } 123 | else if (Math.abs(rect.right - rect2.right) <= this.options.snap) 124 | { 125 | vertical.push({ distance: Math.abs(rect.right - rect2.right), top: rect2.top, height: rect2.height, left: rect2.right, side: 'right', noSpacing: true }) 126 | } 127 | } 128 | } 129 | if (rect.top - this.options.snap <= rect2.bottom && rect.bottom + this.options.snap >= rect2.top) 130 | { 131 | if (Math.abs(rect.left - rect2.right) <= this.options.snap) 132 | { 133 | vertical.push({ distance: Math.abs(rect.left - rect2.right), top: rect2.top, height: rect2.height, left: rect2.right, side: 'left' }) 134 | if (Math.abs(rect.top - rect2.top) <= this.options.snap) 135 | { 136 | horizontal.push({ distance: Math.abs(rect.top - rect2.top), left: rect2.left, width: rect2.width, top: rect2.top, side: 'top', noSpacing: true }) 137 | } 138 | else if (Math.abs(rect.bottom - rect2.bottom) <= this.options.snap) 139 | { 140 | horizontal.push({ distance: Math.abs(rect.bottom - rect2.bottom), left: rect2.left, width: rect2.width, top: rect2.bottom, side: 'bottom', noSpacing: true }) 141 | } 142 | } 143 | else if (Math.abs(rect.right - rect2.left) <= this.options.snap) 144 | { 145 | vertical.push({ distance: Math.abs(rect.right - rect2.left), top: rect2.top, height: rect2.height, left: rect2.left, side: 'right' }) 146 | if (Math.abs(rect.top - rect2.top) <= this.options.snap) 147 | { 148 | horizontal.push({ distance: Math.abs(rect.top - rect2.top), left: rect2.left, width: rect2.width, top: rect2.top, side: 'top', noSpacing: true }) 149 | } 150 | else if (Math.abs(rect.bottom - rect2.bottom) <= this.options.snap) 151 | { 152 | horizontal.push({ distance: Math.abs(rect.bottom - rect2.bottom), left: rect2.left, width: rect2.width, top: rect2.bottom, side: 'bottom', noSpacing: true }) 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | move(win) 161 | { 162 | if (this.stopped || win.options.noSnap || win.isModal()) 163 | { 164 | return 165 | } 166 | this.horizontal.style.display = 'none' 167 | this.vertical.style.display = 'none' 168 | const horizontal = [] 169 | const vertical = [] 170 | const rect = win.win.getBoundingClientRect() 171 | if (this.options.screen) 172 | { 173 | this.screenMove(rect, horizontal, vertical) 174 | } 175 | if (this.options.windows) 176 | { 177 | this.windowsMove(win, rect, horizontal, vertical) 178 | } 179 | if (horizontal.length) 180 | { 181 | horizontal.sort((a, b) => { return a.distance - b.distance }) 182 | const find = horizontal[0] 183 | this.horizontal.style.display = 'block' 184 | this.horizontal.style.width = find.width + 'px' 185 | this.horizontal.y = find.top - this.options.indicator / 2 186 | this.horizontal.style.transform = `translate(${find.left}px,${this.horizontal.y}px)` 187 | this.horizontal.side = find.side 188 | this.horizontal.noSpacing = find.noSpacing 189 | this.horizontal.screen = find.screen 190 | } 191 | if (vertical.length) 192 | { 193 | vertical.sort((a, b) => { return a.distance - b.distance }) 194 | const find = vertical[0] 195 | this.vertical.style.display = 'block' 196 | this.vertical.style.height = find.height + 'px' 197 | this.vertical.x = find.left - this.options.indicator / 2 198 | this.vertical.style.transform = `translate(${this.vertical.x}px,${find.top}px)` 199 | this.vertical.side = find.side 200 | this.vertical.noSpacing = find.noSpacing 201 | this.vertical.screen = find.screen 202 | } 203 | } 204 | 205 | moveEnd(win) 206 | { 207 | if (this.stopped) 208 | { 209 | return 210 | } 211 | if (this.horizontal.style.display === 'block') 212 | { 213 | const spacing = this.horizontal.noSpacing ? 0 : this.options.spacing 214 | const adjust = win.minimized ? (win.height - win.height * win.minimized.scaleY) / 2 : 0 215 | switch (this.horizontal.side) 216 | { 217 | case 'top': 218 | win.y = this.horizontal.y - adjust + spacing + this.options.indicator / 2 219 | break 220 | 221 | case 'bottom': 222 | win.bottom = Math.floor(this.horizontal.y + adjust - spacing + this.options.indicator / 2) 223 | break 224 | } 225 | win.attachToScreen('vertical', this.horizontal.screen ? this.horizontal.side : '') 226 | } 227 | if (this.vertical.style.display === 'block') 228 | { 229 | const spacing = this.vertical.noSpacing ? 0 : this.options.spacing 230 | const adjust = win.minimized ? (win.width - win.width * win.minimized.scaleX) / 2 : 0 231 | switch (this.vertical.side) 232 | { 233 | case 'left': 234 | win.x = this.vertical.x - adjust + spacing + this.options.indicator / 2 235 | break 236 | 237 | case 'right': 238 | win.right = Math.floor(this.vertical.x + adjust - spacing + this.options.indicator / 2) 239 | break 240 | } 241 | win.attachToScreen('horziontal', this.vertical.screen ? this.vertical.side : '') 242 | } 243 | this.horizontal.style.display = this.vertical.style.display = 'none' 244 | } 245 | } -------------------------------------------------------------------------------- /docs/jsdoc/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p this.resize()) 50 | } 51 | 52 | /** 53 | * Create a window 54 | * @param {WindowOptions} [options] 55 | * @param {string} [options.title] 56 | * @param {number} [options.x] position 57 | * @param {number} [options.y] position 58 | * @param {boolean} [options.modal] 59 | * @param {(number|*)} [options.id] if not provide, id will be assigned in order of creation (0, 1, 2...) 60 | * @returns {Window} the created window 61 | */ 62 | createWindow(options={}) 63 | { 64 | const win = new Window(this, Object.assign({}, this.defaultOptions, options)) 65 | win.on('open', () => this._open(win)) 66 | win.on('focus', () => this._focus(win)) 67 | win.on('blur', () => this._blur(win)) 68 | win.on('close', () => this._close(win)) 69 | win.win.addEventListener('mousemove', (e) => this._move(e)) 70 | win.win.addEventListener('touchmove', (e) => this._move(e)) 71 | win.win.addEventListener('mouseup', (e) => this._up(e)) 72 | win.win.addEventListener('touchend', (e) => this._up(e)) 73 | if (this._snap && !options.noSnap) 74 | { 75 | this._snap.addWindow(win) 76 | } 77 | win.resizePlacement(this.bounds, this.options.keepInside) 78 | if (win.options.openOnCreate) 79 | { 80 | win.open() 81 | } 82 | return win 83 | } 84 | 85 | /** 86 | * Attach an existing window to the WindowManager 87 | * Note: WindowManager.createWindow is the preferred way to create windows to ensure that all the defaultOptions 88 | * are applied to the Window. If you use this function, then Window needs to be initialized with WindowOptions. 89 | * @param {Window} win 90 | * @returns {Window} the window 91 | */ 92 | attachWindow(win) 93 | { 94 | win.on('open', this._open, this) 95 | win.on('focus', this._focus, this) 96 | win.on('blur', this._blur, this) 97 | win.on('close', this._close, this) 98 | this.win.appendChild(win.win) 99 | win.wm = this 100 | win.win.addEventListener('mousemove', (e) => this._move(e)) 101 | win.win.addEventListener('touchmove', (e) => this._move(e)) 102 | win.win.addEventListener('mouseup', (e) => this._up(e)) 103 | win.win.addEventListener('touchend', (e) => this._up(e)) 104 | if (this._snap && !this.defaultOptions.noSnap) 105 | { 106 | this._snap.addWindow(win) 107 | } 108 | return win 109 | } 110 | 111 | /** 112 | * enable edge and/or screen snapping 113 | * @param {SnapOptions} options 114 | */ 115 | snap(options) 116 | { 117 | this._snap = new Snap(this, options) 118 | for (let win of this.windows) 119 | { 120 | if (!win.options.noSnap) 121 | { 122 | this._snap.addWindow(win) 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * send window to front 129 | * @param {Window} win 130 | */ 131 | sendToFront(win) 132 | { 133 | const index = this.windows.indexOf(win) 134 | console.assert(index !== -1, 'sendToFront should find window in this.windows') 135 | if (index !== this.windows.length - 1) 136 | { 137 | this.windows.splice(index, 1) 138 | this.windows.push(win) 139 | this._reorder() 140 | } 141 | } 142 | 143 | /** 144 | * send window to back 145 | * @param {Window} win 146 | */ 147 | sendToBack(win) 148 | { 149 | const index = this.windows.indexOf(win) 150 | console.assert(index !== -1, 'sendToFront should find window in this.windows') 151 | if (index !== 0) 152 | { 153 | this.windows.splice(index, 1) 154 | this.windows.unshift(win) 155 | this._reorder() 156 | } 157 | } 158 | 159 | /** 160 | * save the state of all the windows 161 | * @returns {object} use this object in load() to restore the state of all windows 162 | */ 163 | save() 164 | { 165 | const data = {} 166 | for (let i = 0; i < this.windows.length; i++) 167 | { 168 | const entry = this.windows[i] 169 | data[entry.id] = entry.save() 170 | data[entry.id].order = i 171 | } 172 | return data 173 | } 174 | 175 | /** 176 | * restores the state of all the windows 177 | * NOTE: this requires that the windows have the same id as when save() was called 178 | * @param {object} data created by save() 179 | */ 180 | load(data) 181 | { 182 | for (let i = 0; i < this.windows.length; i++) 183 | { 184 | const entry = this.windows[i] 185 | if (data[entry.id]) 186 | { 187 | entry.load(data[entry.id]) 188 | } 189 | } 190 | // reorder windows 191 | } 192 | 193 | /** 194 | * close all windows 195 | */ 196 | closeAll() 197 | { 198 | for (let win of this.windows) 199 | { 200 | win.close() 201 | } 202 | this.windows = [] 203 | this.active = null 204 | } 205 | 206 | /** 207 | * reorder windows 208 | * @private 209 | * @returns {number} available z-index for top window 210 | */ 211 | _reorder() 212 | { 213 | let i = 0 214 | for (const win of this.windows) 215 | { 216 | if (!win.isClosed()) 217 | { 218 | win.z = i++ 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * @param {HTMLElement} parent 225 | */ 226 | _createDom(parent) 227 | { 228 | /** 229 | * This is the top-level DOM element 230 | * @type {HTMLElement} 231 | * @readonly 232 | */ 233 | this.win = html({ 234 | parent, styles: { 235 | 'user-select': 'none', 236 | 'width': '100%', 237 | 'height': '100%', 238 | 'overflow': 'hidden', 239 | 'z-index': -1, 240 | 'cursor': 'default' 241 | } 242 | }) 243 | 244 | /** 245 | * This is the bottom DOM element. Use this to set a wallpaper or attach elements underneath the windows 246 | * @type {HTMLElement} 247 | * @readonly 248 | */ 249 | this.overlay = html({ 250 | parent: this.win, styles: { 251 | 'user-select': 'none', 252 | 'position': 'absolute', 253 | 'top': 0, 254 | 'left': 0, 255 | 'width': '100%', 256 | 'height': '100%', 257 | 'overflow': 'hidden' 258 | } 259 | }) 260 | this.overlay.addEventListener('mousemove', (e) => this._move(e)) 261 | this.overlay.addEventListener('touchmove', (e) => this._move(e)) 262 | this.overlay.addEventListener('mouseup', (e) => this._up(e)) 263 | this.overlay.addEventListener('touchend', (e) => this._up(e)) 264 | 265 | this.modalOverlay = html({ 266 | parent: this.win, 267 | styles: { 268 | 'display': 'none', 269 | 'user-select': 'none', 270 | 'position': 'absolute', 271 | 'top': 0, 272 | 'left': 0, 273 | 'width': '100%', 274 | 'height': '100%', 275 | 'overflow': 'hidden', 276 | 'background': this.defaultOptions.backgroundModal 277 | } 278 | }) 279 | this.modalOverlay.addEventListener('mousemove', (e) => { this._move(e); e.preventDefault(); e.stopPropagation() }) 280 | this.modalOverlay.addEventListener('touchmove', (e) => { this._move(e); e.preventDefault(); e.stopPropagation() }) 281 | this.modalOverlay.addEventListener('mouseup', (e) => { this._up(e); e.preventDefault(); e.stopPropagation() }) 282 | this.modalOverlay.addEventListener('touchend', (e) => { this._up(e); e.preventDefault(); e.stopPropagation() }) 283 | this.modalOverlay.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation() }) 284 | this.modalOverlay.addEventListener('touchstart', (e) => { e.preventDefault(); e.stopPropagation() }) 285 | } 286 | 287 | _open(win) 288 | { 289 | this.windows.push(win) 290 | this._reorder() 291 | if (win.options.modal) 292 | { 293 | this.modalOverlay.style.display = 'block' 294 | this.modalOverlay.style.zIndex = win.z 295 | } 296 | else 297 | { 298 | this.modalOverlay.style.display = 'none' 299 | } 300 | } 301 | 302 | _focus(win) 303 | { 304 | if (this.active === win) 305 | { 306 | return 307 | } 308 | if (this.active) 309 | { 310 | this.active.blur() 311 | } 312 | const index = this.windows.indexOf(win) 313 | console.assert(index !== -1, 'WindowManager._focus should find window in this.windows') 314 | if (index !== this.windows.length - 1) 315 | { 316 | this.windows.splice(index, 1) 317 | this.windows.push(win) 318 | } 319 | this._reorder() 320 | this.active = this.windows[this.windows.length - 1] 321 | } 322 | 323 | _blur(win) 324 | { 325 | if (this.active === win) 326 | { 327 | this.active = null 328 | } 329 | } 330 | 331 | _close(win) 332 | { 333 | const index = this.windows.indexOf(win) 334 | console.assert(index !== -1, 'WindowManager._close should find window in this.windows') 335 | this.windows.splice(index, 1) 336 | const next = this.windows[this.windows.length - 1] 337 | if (win.isModal(true)) 338 | { 339 | if (next && next.isModal()) 340 | { 341 | this.modalOverlay.style.zIndex = next.z 342 | } 343 | else 344 | { 345 | this.modalOverlay.style.display = 'none' 346 | } 347 | } 348 | next.focus() 349 | } 350 | 351 | _move(e) 352 | { 353 | for (const key in this.windows) 354 | { 355 | this.windows[key]._move(e) 356 | } 357 | } 358 | 359 | _up(e) 360 | { 361 | for (const key in this.windows) 362 | { 363 | this.windows[key]._up(e) 364 | } 365 | } 366 | 367 | checkModal(win) 368 | { 369 | return !this.modal || this.modal === win 370 | } 371 | 372 | /** @type {Bounds} */ 373 | get bounds() 374 | { 375 | return { 376 | top: this.win.offsetTop, 377 | bottom: this.win.offsetTop + this.win.offsetHeight, 378 | left: this.win.offsetLeft, 379 | right: this.win.offsetLeft + this.win.offsetWidth 380 | } 381 | } 382 | 383 | resize() 384 | { 385 | const bounds = this.bounds 386 | for (const key in this.windows) 387 | { 388 | this.windows[key].resizePlacement(bounds, this.options.keepInside) 389 | } 390 | } 391 | } 392 | 393 | /** 394 | * @typedef {object} SnapOptions 395 | * @property {boolean} [screen=true] snap to screen edges 396 | * @property {boolean} [windows=true] snap to window edges 397 | * @property {number} [snap=20] distance to edge before snapping 398 | * @property {string} [color=#a8f0f4] color for snap bars 399 | * @property {number} [spacing=0] spacing distance between window and edges 400 | */ 401 | 402 | /** 403 | * @typedef {object} Bounds 404 | * @property {number} left 405 | * @property {number} right 406 | * @property {number} top 407 | * @property {number} bottom 408 | */ -------------------------------------------------------------------------------- /docs/jsdoc/module-EventEmitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 247 |
248 |

Module: EventEmitter

249 | 250 | 251 | 252 | 253 |
254 | 255 |
256 |
257 |

EventEmitter 258 |

259 | 260 |
261 |
262 | 263 |
264 |
265 | 266 | 267 | 268 | 269 | 270 | 271 |
272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 |
292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 |
318 | 319 |
320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
328 | 331 |
332 |
333 |
334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /docs/jsdoc/external-EventEmitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 247 |
248 |

External: EventEmitter

249 | 250 | 251 | 252 | 253 |
254 | 255 |
256 |
257 |

EventEmitter 258 |

259 | 260 |
261 |
262 | 263 |
264 |
265 | 266 | 267 | 268 | 269 | 270 | 271 |
272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 |
292 | 293 | 294 | 295 | 296 |
297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 |
318 | 319 |
320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
328 | 331 |
332 |
333 |
334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /docs/jsdoc/window-options.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 175 |
176 |

Source: window-options.js

177 | 178 | 179 | 180 | 181 |
182 |
183 |
184 |

window-options.js

185 |
186 |
187 |
188 |
/**
189 |  * @typedef {object} Window~WindowOptions
190 |  * @property {number} [x=0]
191 |  * @property {number} [y=0]
192 |  * @property {number} [width]
193 |  * @property {number} [height]
194 |  * @property {boolean} [movable=true]
195 |  * @property {boolean} [resizable=true]
196 |  * @property {boolean} [maximizable=true]
197 |  * @property {boolean} [minimizable=true]
198 |  * @property {boolean} [closable=true]
199 |  * @property {boolean} [titlebar=true]
200 |  * @property {string} [titlebarHeight=36px]
201 |  * @property {string} [minWidth=200px]
202 |  * @property {string} [minHeight=60px]
203 |  * @property {string} [borderRadius=4px]
204 |  * @property {number} [minimizeSize=50]
205 |  * @property {string} [shadow='0 0 12px 1px rgba(0, 0, 0, 0.6)']
206 |  * @property {number} [animateTime=250]
207 |  * @property {(string|function)} [ease] easing name (see {@link https://www.npmjs.com/package/penner} for list or function)
208 |  * @property {string} [backgroundColorWindow=#fefefe]
209 |  * @property {string} [backgroundColorTitlebarActive=#365d98]
210 |  * @property {string} [backgroundColorTitlebarInactive=#888888]
211 |  * @property {string} [foregroundColorButton=#ffffff]
212 |  * @property {string} [foregroundColorTitle=#ffffff]
213 |  * @property {string} [backgroundMinimizeButton=...]
214 |  * @property {string} [backgroundMaximizeButton=...]
215 |  * @property {string} [backgroundCloseButton=...]
216 |  * @property {string} [backgroundResize=...]
217 |  */
218 | const WindowOptions = {
219 |     x: 0,
220 |     y: 0,
221 | 
222 |     minWidth: '200px',
223 |     minHeight: '60px',
224 | 
225 |     borderRadius: '4px',
226 |     minimizeSize: 50,
227 |     shadow: '0 0 12px 1px rgba(0, 0, 0, 0.6)',
228 |     movable: true,
229 |     resizable: true,
230 |     maximizable: true,
231 |     minimizable: true,
232 |     closable: true,
233 | 
234 |     titlebar: true,
235 |     titlebarHeight: '36px',
236 | 
237 |     animateTime: 250,
238 |     ease: 'easeInOutSine',
239 | 
240 |     backgroundColorWindow: '#fefefe',
241 |     backgroundColorTitlebarActive: '#365d98',
242 |     backgroundColorTitlebarInactive: '#888888',
243 |     foregroundColorButton: '#ffffff',
244 |     foregroundColorTitle: '#ffffff',
245 | 
246 |     backgroundCloseButton: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAfElEQVQ4jdXUwQ2AIBBEUULLVkET2N228b1gVFxkIWuik3gbHhCQEH4TYAEESEA09GPpCrBoBeFIfkILlk990UqJa1RUwQCSZdYbaumY0WGsg67lG8M66BxWofWq9tU2sbFZZuO6ZddDaWBz18YyYAjlhV/P/XHwfb4+mw0FvmLroLRViAAAAABJRU5ErkJggg==)',
247 |     backgroundMaximizeButton: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAVklEQVQ4jWNgoAX4////1v+Ug60MDAwMjFAD/1PDYYyMjIxM1DAIGVDdQBY0vgmZ5pxB4cFClUzDUPQP/jAcNXDUwMFgIEpepkYxBnPhNkoNopIZmAAAdghhoHiIrEcAAAAASUVORK5CYII=)',
248 |     backgroundMinimizeButton: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAOElEQVQ4jWNgGAWjYBSMgiEJGGGM////p1FkECPjLHQD/1NoICMDAwMDEyWGYAMsSOxz1DacKgAAbrQI132oX0IAAAAASUVORK5CYII=)',
249 |     backgroundRestoreButton: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAA2klEQVQ4ja2UzQ3CMAyFnyuuSD0wBjvAKGUDRmAERugO5U6G6ABw51CJAR6XBIX8OCHquyR27U9pnltgZYnbkNwB2Ff2zSLyUitITqzXlON03n5beTq1dhPETwBjATZoD0PgQ0QuWgPJ4z9AvzFnUp8AxyaRNCSNFzeZ1CGvJpOyr2yVMmmw6xjEVcDIJHd3Lh+aFAJ7ryB1+S6/5E7gA98ADgDuQU0YA8CtBnjC75hc7XpO9M1FoJ0j42KSi82bqEuRNjZNKrncJ0yJaqCY9FXrlyIKcN0fbqs+F7nRockDNMcAAAAASUVORK5CYII=)',
250 | 
251 |     backgroundResize: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzREODAwQzcyRjZDMTFFMjg5NkREMENBNjJERUE4Q0IiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzREODAwQzgyRjZDMTFFMjg5NkREMENBNjJERUE4Q0IiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDNEQ4MDBDNTJGNkMxMUUyODk2REQwQ0E2MkRFQThDQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDNEQ4MDBDNjJGNkMxMUUyODk2REQwQ0E2MkRFQThDQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuQy0VQAAACLSURBVHjaYpw9ezYDEUARiO8zEaHQHohPArEcCxEK1wGxPxA/wmeyDZLCIyABJjwKNwJxEFShIi7FyAoPArEZEB8DYi0mHFaHIikEaUwE4mtMWBRGAPE+NIU7kJ0BUxiNQyFInpMJKgFTuBuLQj8gXg3yJCicHyFZDQJfgDgOqhEE3gGxD8jNAAEGADlXJQUd3J75AAAAAElFTkSuQmCC) no-repeat',
252 | }
253 | 
254 | module.exports = WindowOptions
255 |
256 |
257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 |
266 | 269 |
270 |
271 |
272 | 273 | 274 | 275 | 276 | -------------------------------------------------------------------------------- /docs/jsdoc/src_html.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 309 |
310 | 311 |

Source: src/html.js

312 | 313 | 314 | 315 | 316 | 317 |
318 |
319 |
320 |

src/html.js

321 |
322 |
323 |
324 |
/**
325 |  * shortcut to create an html element
326 |  * @param {object} options
327 |  * @param {type} [options.string=div]
328 |  * @param {string} [options.className]
329 |  * @param {object} [options.styles]
330 |  * @param {HTMLElement} [options.parent]
331 |  * @param {string} [options.html]
332 |  * @returns {HTMLElement}
333 |  */
334 | export function html(options={})
335 | {
336 |     const object = document.createElement(options.type || 'div')
337 |     if (options.parent)
338 |     {
339 |         options.parent.appendChild(object)
340 |     }
341 |     if (options.styles)
342 |     {
343 |         Object.assign(object.style, options.styles)
344 |     }
345 |     if (options.className)
346 |     {
347 |         object.className = options.className
348 |     }
349 |     if (options.html)
350 |     {
351 |         object.innerHTML = options.html
352 |     }
353 |     return object
354 | }
355 |
356 |
357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 |
366 | 369 |
370 |
371 |
372 | 373 | 374 | 375 | 376 | -------------------------------------------------------------------------------- /docs/jsdoc/EventEmitter-.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 309 |
310 | 311 |

Class: EventEmitter

312 | 313 | 314 | 315 | 316 | 317 |
318 | 319 |
320 |
321 |

EventEmitter 322 |

323 | 324 |
325 |
326 | 327 |
328 |
329 | 330 | 331 | 332 | 333 |
334 |
335 |

336 | 337 | 338 | new EventEmitter 339 | 340 | () 341 | 342 | 343 | 344 | 345 |

346 | 347 | 348 |
349 | index.js:92 350 |
351 | 352 |
353 | 354 | 355 |
356 |
357 | 358 | 359 |
360 | Minimal `EventEmitter` interface that is molded against the Node.js 361 | `EventEmitter` interface. 362 |
363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 |
373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 |
393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 |
407 | 408 | 409 |
410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 |
431 | 432 |
433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 |
441 | 444 |
445 |
446 |
447 | 448 | 449 | 450 | -------------------------------------------------------------------------------- /docs/jsdoc/window-manager.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 175 |
176 |

Source: window-manager.js

177 | 178 | 179 | 180 | 181 |
182 |
183 |
184 |

window-manager.js

185 |
186 |
187 |
188 |
const exists = require('exists')
189 | 
190 | const html = require('./html')
191 | const Window = require('./window')
192 | const WindowOptions = require('./window-options')
193 | 
194 | /**
195 |  * Creates a windowing system to create and manage windows
196 |  *
197 |  * @extends EventEmitter
198 |  * @example
199 |  * var wm = new WindowManager();
200 |  *
201 |  * wm.createWindow({ x: 20, y: 20, width: 200 });
202 |  * wm.content.innerHTML = 'Hello there!';
203 |  */
204 | class WindowManager
205 | {
206 |     /**
207 |      * @param {WindowOptions} [defaultOptions] default WindowOptions used when createWindow is called
208 |      * @param {boolean} [defaultOptions.quiet] suppress the simple-window-manager console message
209 |      */
210 |     constructor(defaultOptions)
211 |     {
212 |         this._createDom()
213 |         this.windows = []
214 |         this.active = null
215 |         this.modal = null
216 |         this.options = {}
217 |         for (let key in WindowOptions)
218 |         {
219 |             this.options[key] = WindowOptions[key]
220 |         }
221 |         if (defaultOptions)
222 |         {
223 |             for (let key in defaultOptions)
224 |             {
225 |                 this.options[key] = defaultOptions[key]
226 |             }
227 |         }
228 |         if (!defaultOptions.quiet)
229 |         {
230 |             console.log('%c ☕ simple-window-manager initialized ☕', 'color: #ff00ff')
231 |         }
232 |     }
233 | 
234 |     /**
235 |      * Create a window
236 |      * @param {WindowOptions} [options]
237 |      * @param {string} [options.title]
238 |      * @param {number} [options.x] position
239 |      * @param {number} [options.y] position
240 |      * @param {boolean} [options.modal]
241 |      * @param {Window} [options.center] center in the middle of an existing Window
242 |      * @param {string|number} [options.id] if not provide, id will be assigned in order of creation (0, 1, 2...)
243 |      * @fires open
244 |      * @fires focus
245 |      * @fires blur
246 |      * @fires close
247 |      * @fires maximize
248 |      * @fires maximize-restore
249 |      * @fires minimize
250 |      * @fires minimize-restore
251 |      * @fires move
252 |      * @fires move-start
253 |      * @fires move-end
254 |      * @fires resize
255 |      * @fires resize-start
256 |      * @fires resize-end
257 |      */
258 |     createWindow(options)
259 |     {
260 |         options = options || {}
261 |         for (let key in this.options)
262 |         {
263 |             if (!exists(options[key]))
264 |             {
265 |                 options[key] = this.options[key]
266 |             }
267 |         }
268 |         const win = new Window(this, options);
269 |         win.on('open', this._open, this)
270 |         win.on('focus', this._focus, this)
271 |         win.on('blur', this._blur, this)
272 |         win.on('close', this._close, this)
273 |         win.win.addEventListener('mousemove', (e) => this._move(e))
274 |         win.win.addEventListener('touchmove', (e) => this._move(e))
275 |         win.win.addEventListener('mouseup', (e) => this._up(e))
276 |         win.win.addEventListener('touchend', (e) => this._up(e))
277 |         if (options.center)
278 |         {
279 |             win.move(
280 |                 options.center.x + options.center.width / 2 - (options.width ? options.width / 2 : 0),
281 |                 options.center.y + options.center.height / 2 - (options.height ? options.height / 2 : 0)
282 |             )
283 |         }
284 |         if (options.modal)
285 |         {
286 |             this.modal = win
287 |         }
288 |         return win
289 |     }
290 | 
291 |     /**
292 |      * send window to front
293 |      * @param {Window} win
294 |      */
295 |     sendToFront(win)
296 |     {
297 |         const index = this.windows.indexOf(win)
298 |         if (index !== this.windows.length - 1)
299 |         {
300 |             this.windows.splice(index, 1)
301 |             this.windows.push(win)
302 |             this._reorder()
303 |         }
304 |     }
305 | 
306 |     /**
307 |      * send window to back
308 |      * @param {Window} win
309 |      */
310 |     sendToBack(win)
311 |     {
312 |         const index = this.windows.indexOf(win)
313 |         if (index !== 0)
314 |         {
315 |             this.windows.splice(index, 1)
316 |             this.windows.unshift(win)
317 |             this._reorder()
318 |         }
319 |     }
320 | 
321 |     /**
322 |      * save the state of all the windows
323 |      * @returns {object} use this object in load() to restore the state of all windows
324 |      */
325 |     save()
326 |     {
327 |         const data = {}
328 |         for (let i = 0; i < this.windows.length; i++)
329 |         {
330 |             const entry = this.windows[i]
331 |             data[entry.id] = entry.save()
332 |             data[entry.id].order = i
333 |         }
334 |         return data
335 |     }
336 | 
337 |     /**
338 |      * restores the state of all the windows
339 |      * NOTE: this requires that the windows have the same id as when save() was called
340 |      * @param {object} data created by save()
341 |      */
342 |     load(data)
343 |     {
344 |         for (let i = 0; i < this.windows.length; i++)
345 |         {
346 |             const entry = this.windows[i]
347 |             if (data[entry.id])
348 |             {
349 |                 entry.load(data[entry.id])
350 |             }
351 |         }
352 |         // reorder windows
353 |     }
354 | 
355 |     /**
356 |      * reorder windows
357 |      * @private
358 |      * @returns {number} available z-index for top window
359 |      */
360 |     _reorder()
361 |     {
362 |         let i = 0
363 |         for (; i < this.windows.length; i++)
364 |         {
365 |             this.windows[i].z = i
366 |         }
367 |     }
368 | 
369 |     _createDom()
370 |     {
371 |         this.win = html.create({
372 |             parent: document.body, styles: {
373 |                 'user-select': 'none',
374 |                 'width': '100%',
375 |                 'height': '100%',
376 |                 'overflow': 'hidden',
377 |                 'z-index': -1,
378 |                 'cursor': 'default'
379 |             }
380 |         })
381 |         this.overlay = html.create({
382 |             parent: this.win, styles: {
383 |                 'user-select': 'none',
384 |                 'position': 'absolute',
385 |                 'top': 0,
386 |                 'left': 0,
387 |                 'width': '100%',
388 |                 'height': '100%',
389 |                 'overflow': 'hidden'
390 |             }
391 |         })
392 |         this.overlay.addEventListener('mousemove', (e) => this._move(e))
393 |         this.overlay.addEventListener('touchmove', (e) => this._move(e))
394 |         this.overlay.addEventListener('mouseup', (e) => this._up(e))
395 |         this.overlay.addEventListener('touchend', (e) => this._up(e))
396 |     }
397 | 
398 |     _open(win)
399 |     {
400 |         const index = this.windows.indexOf(win)
401 |         if (index === -1)
402 |         {
403 |             this.windows.push(win)
404 |         }
405 |     }
406 | 
407 |     _focus(win)
408 |     {
409 |         if (this.active === win)
410 |         {
411 |             return
412 |         }
413 | 
414 |         if (this.active)
415 |         {
416 |             this.active.blur()
417 |         }
418 | 
419 |         const index = this.windows.indexOf(win)
420 |         if (index !== this.windows.length - 1)
421 |         {
422 |             this.windows.splice(index, 1)
423 |             this.windows.push(win)
424 |         }
425 |         this._reorder()
426 | 
427 |         this.active = win
428 |     }
429 | 
430 |     _blur(win)
431 |     {
432 |         if (this.active === win)
433 |         {
434 |             this.active = null
435 |         }
436 |     }
437 | 
438 |     _close(win)
439 |     {
440 |         if (this.modal === win)
441 |         {
442 |             this.modal = null
443 |         }
444 |         const index = this.windows.indexOf(win)
445 |         if (index !== -1)
446 |         {
447 |             this.windows.splice(index, 1)
448 |         }
449 |         if (this.active === win)
450 |         {
451 |             this._blur(win)
452 |         }
453 |     }
454 | 
455 |     _move(e)
456 |     {
457 |         for (let key in this.windows)
458 |         {
459 |             this.windows[key]._move(e)
460 |         }
461 |     }
462 | 
463 |     _up(e)
464 |     {
465 |         for (let key in this.windows)
466 |         {
467 |             this.windows[key]._up(e)
468 |         }
469 |     }
470 | 
471 |     _checkModal(win)
472 |     {
473 |         return !this.modal || this.modal === win
474 |     }
475 | }
476 | 
477 | /**
478 |  * @external EventEmitter
479 |  */
480 | 
481 | 
482 | module.exports = WindowManager
483 |
484 |
485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 |
494 | 497 |
498 |
499 |
500 | 501 | 502 | 503 | 504 | -------------------------------------------------------------------------------- /docs/jsdoc/src_windowOptions.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | simple-window-manager API Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 309 |
310 | 311 |

Source: src/windowOptions.js

312 | 313 | 314 | 315 | 316 | 317 |
318 |
319 |
320 |

src/windowOptions.js

321 |
322 |
323 |
324 |
import { close, maximize, restore, resize } from './images'
325 | 
326 | /**
327 |  * @typedef {object} WindowOptions
328 |  * @property {number} [x=0]
329 |  * @property {number} [y=0]
330 |  * @property {number} [width]
331 |  * @property {number} [height]
332 |  * @property {boolean} [modal]
333 |  * @property {boolean} [openOnCreate=true]
334 |  * @property {boolean} [movable=true]
335 |  * @property {boolean} [resizable=true]
336 |  * @property {boolean} [maximizable=true]
337 |  * @property {boolean} [closable=true]
338 |  * @property {boolean} [noSnap] don't snap this window or use this window as a snap target
339 |  * @property {boolean} [titlebar=true]
340 |  * @property {string} [titlebarHeight=36px]
341 |  * @property {boolean} [titleCenter]
342 |  * @property {string} [minWidth=200px]
343 |  * @property {string} [minHeight=60px]
344 |  * @property {string} [borderRadius=4px]
345 |  * @property {object} [styles]
346 |  * @property {string} [shadow='0 0 12px 1px rgba(0, 0, 0, 0.6)']
347 |  * @property {number} [animateTime=250]
348 |  * @property {string} [backgroundModal=rgba(0,0,0,0.6)]
349 |  * @property {string} [backgroundWindow=#fefefe]
350 |  * @property {string} [backgroundTitlebarActive=#365d98]
351 |  * @property {string} [backgroundTitlebarInactive=#888888]
352 |  * @property {string} [foregroundButton=#ffffff]
353 |  * @property {string} [foregroundTitle=#ffffff]
354 |  * @property {string} [maximizeButton=...]
355 |  * @property {string} [closeButton=...]
356 |  * @property {string} [resize=...]
357 |  */
358 | export const windowOptions = {
359 |     x: 0,
360 |     y: 0,
361 |     width: undefined,
362 |     height: undefined,
363 |     modal: false,
364 |     openOnCreate: true,
365 | 
366 |     classNames: {},
367 | 
368 |     minWidth: '200px',
369 |     minHeight: '60px',
370 |     borderRadius: 0,
371 |     styles: {},
372 | 
373 |     shadow: 'none',
374 |     movable: true,
375 |     resizable: true,
376 |     maximizable: true,
377 |     closable: true,
378 | 
379 |     titlebar: true,
380 |     titlebarHeight: '2rem',
381 | 
382 |     backgroundModal: 'rgba(0, 0, 0, 0.6)',
383 |     backgroundWindow: '#fefefe',
384 |     backgroundTitlebarActive: '#365d98',
385 |     backgroundTitlebarInactive: '#888888',
386 |     foregroundButton: '#ffffff',
387 |     foregroundTitle: '#ffffff',
388 | 
389 |     closeButton: close,
390 |     maximizeButton: maximize,
391 |     restoreButton: restore,
392 | 
393 |     backgroundResize: resize
394 | }
395 |
396 |
397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 |
406 | 409 |
410 |
411 |
412 | 413 | 414 | 415 | 416 | --------------------------------------------------------------------------------