├── img ├── icon16.png ├── icon32.png ├── icon64.png └── screenshot.jpg ├── manifest.json ├── js ├── background.js ├── options.js └── spatial-navigation.js ├── README.md ├── options.html └── css └── options.css /img/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pathduck/spatialnavigation/HEAD/img/icon16.png -------------------------------------------------------------------------------- /img/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pathduck/spatialnavigation/HEAD/img/icon32.png -------------------------------------------------------------------------------- /img/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pathduck/spatialnavigation/HEAD/img/icon64.png -------------------------------------------------------------------------------- /img/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pathduck/spatialnavigation/HEAD/img/screenshot.jpg -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "content_scripts": [ 3 | { 4 | "js": ["js/spatial-navigation.js"], 5 | "matches": [""], 6 | "run_at": "document_end" 7 | } 8 | ], 9 | "background": { 10 | "service_worker": "js/background.js" 11 | }, 12 | "icons": { 13 | "16": "img/icon16.png", 14 | "32": "img/icon32.png", 15 | "64": "img/icon64.png" 16 | }, 17 | "description": "Smart Spatial Navigation extension with key map to links. Binds WASD and Q/E/F keys.", 18 | "manifest_version": 3, 19 | "name": "Spatial Navigation", 20 | "permissions": ["storage"], 21 | "host_permissions": [""], 22 | "short_name": "spatial-navigation", 23 | "version": "0.5", 24 | "options_page": "options.html" 25 | } 26 | -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(function () { 2 | let settings = { 3 | hotkeys: { 4 | codes: { 5 | navUp: [["w"]], 6 | navLeft: [["a"]], 7 | navDown: [["s"]], 8 | navRight: [["d"]], 9 | expand: [["e"]], 10 | contract: [["shift", "e"]], 11 | quit: [["q"]], 12 | click: [["f"], ["enter"]], 13 | shiftClick: [["shift", "enter"], ["shift", "f"]], 14 | controlClick: [["control", "enter"], ["control", "f"]], 15 | disableKeys: [["control", "shift", "x"]], 16 | }, 17 | }, 18 | }; 19 | 20 | chrome.storage.local.set({ "extension-settings": settings }, function () { 21 | console.log("Initial setting set..."); 22 | }); 23 | }); 24 | 25 | chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) { 26 | if (msg.from === "content" && msg.subject === "changeIcon") { 27 | // let canvas = document.getElementById("icon-making-canvas"); 28 | // if (!canvas) { 29 | // canvas = document.createElement("CANVAS"); 30 | // canvas.id = "icon-making-canvas"; 31 | // canvas.width = canvas.height = 16; 32 | // } 33 | // let ctx = canvas.getContext("2d"); 34 | // ctx.beginPath(); 35 | // ctx.rect(0, 0, 16, 16); 36 | // ctx.fillStyle = "red"; 37 | // ctx.fill(); 38 | // ctx.fillStyle = "white"; 39 | // ctx.textAlign = "center"; 40 | // ctx.textBaseline = "middle"; 41 | // fitTextOnCanvas(ctx, msg.speed, "Roboto Condensed", 9, canvas.width); 42 | // let imgData = ctx.getImageData(0, 0, 16, 16); 43 | // chrome.pageAction.setIcon({ imageData: imgData, tabId: sender.tab.id }); 44 | } 45 | return Promise.resolve("Dummy response to keep the console quiet"); 46 | }); 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spatialnavigation 2 | 3 | **Spatial Navigation Chrome Extension** 4 | 5 | Smart spatial navigation with key map to links. 6 | 7 | Based off the *[Spatial Navigation](https://github.com/falsandtru/spatial-navigation)* extension by Falsandtru. 8 | 9 | Binds WASD, F, E and Q keys for navigation/hints mode. 10 | 11 | - `W/A/S/D`: Four-way spatial navigation 12 | - `E/Shift+E`: Expand/Contract (change link targets) 13 | - `Q`: Quit navigation/hints mode 14 | - `Enter/F`: Click link 15 | - `Shift+Enter/Shift+F`: Shift-click link (open new tab) 16 | - `Ctrl+Enter/Ctrl+F`: Ctrl-click link (open new background tab) 17 | - `Ctrl+Shift+X`: Disable the hotkeys for current tab 18 | 19 | Set your own keys: Click the icon and choose Options, or visit the details page in the browser extension manager. 20 | 21 | Any contributions are most welcome! 🤓👍 22 | 23 | ### Screenshot 24 | 25 | ![Screenshot of extension in use](img/screenshot.jpg "Screenshot of extension in use") 26 | 27 | ### Installation 28 | 29 | - Clone this repo, or download and unpack it to your file system. 30 | - Open your browser's *Manage Extensions* page. 31 | - Enable Developer Mode. 32 | - Click 'Load Unpacked'. 33 | - In the file picker navigate to the extension's folder containing the `manifest.json` file and click 'Select Folder'. 34 | - The extension is now installed. 35 | 36 | To update the extension do a `git pull` in the repo folder, or download again and unzip to the same folder.\ 37 | Then click the Reload button or disable then enable it again to update. 38 | 39 | ### Notes 40 | 41 | This extension is far from feature complete, and some things just don't work as expected. 42 | The inherited code is also a bit of a mess, and since I am no JS coder there's limits to what can be done. 43 | As this started as a very personal extension for my use, some things are the way I want them and not easily changed. 44 | 45 | Here are some of the more common issues/bugs/TODOs and some tips: 46 | 47 | - Changing keys in options require a tab reload to take effect. 48 | 49 | - Using `Ctrl+` in hints mode does not work. Using `Shift+` works for opening in a new tab though. 50 | 51 | - The default `Ctrl+F` interferes with the common browser shortcut for Find-In-Page. I suggest using something else for Find, like F3, or changing the key to for instance `G`. 52 | 53 | - Even if keys are changed, there might be conflicts with the link hints as they are hard-coded, see below. 54 | 55 | - The extension uses a hard-coded list of link hint keys. These might interfere with common browser keys, especially if single-key shortcuts are used in the browser. If so these will need to be removed from the list. 56 | 57 | - The list of available link hint keys are in `spatial-navigation.js`:\ 58 | `var keys = 'abcdfghijklmnoprstuvwxyz'`\ 59 | You can edit this string and reload the extension. You can also add keys you want to use instead, for instance the number keys (if not used for anything else). 60 | 61 | - The CSS styles for links/hints/url display can if necessary be changed by editing the following values in `spatial-navigation.js`:\ 62 | `this.style.innerHTML`\ 63 | `marker.style.cssText`\ 64 | `display.style.cssText` 65 | 66 | - There are no language options or internationalization. 67 | -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spatial Navigation Options 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 |

Spatial Navigation Options

19 |
20 |
21 | 22 |
23 |

Keyboard Shortcuts

24 |
25 |
26 |
27 | 32 |
33 | 38 |
39 | 44 |
45 | 50 |
51 |
52 | 53 | Expand (Map Keys or Change Targets) 54 | 55 |
56 |
57 |
58 | 59 | Contract 60 | 61 |
62 |
63 |
64 | 65 | Quit 66 | 67 |
68 |
69 |
70 | 71 | Click 72 | 73 |
74 |
75 |
76 | 77 | Shift Click 78 | 79 |
80 |
81 |
82 | 83 | Control Click 84 | 85 |
86 |
87 |
88 | 89 | Disable the Hotkeys 90 | 91 |
92 |
93 |
94 | 95 |
96 |
97 | 98 | Reset All Settings to Defaults 99 | 100 |
101 |
102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /css/options.css: -------------------------------------------------------------------------------- 1 | html * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background: black; 7 | color: #fafafa; 8 | margin: 0px !important; 9 | } 10 | 11 | @media only screen and (max-width: 730px) { 12 | body { 13 | padding: 10px; 14 | } 15 | } 16 | 17 | h1, 18 | h2 { 19 | color: #fafafa; 20 | font-family: system-ui; 21 | font-weight: 500; 22 | } 23 | 24 | h1 { 25 | font-size: 30px; 26 | } 27 | 28 | h2 { 29 | font-size: 18px; 30 | } 31 | 32 | .title { 33 | padding: 15px 0 2px 0; 34 | text-align: center; 35 | margin-bottom: -20px; 36 | } 37 | 38 | .title-area { 39 | display: flex; 40 | justify-content: space-between; 41 | position: relative; 42 | max-width: 700px; 43 | margin: 20px auto 2px auto; 44 | } 45 | 46 | .setting-area { 47 | padding-left: 10px; 48 | } 49 | 50 | .card { 51 | padding: 12px 18px; 52 | background-color: #171b1f; 53 | border: 1px solid #3e4852; 54 | border-radius: 5px; 55 | font-family: system-ui; 56 | max-width: 700px; 57 | margin: 0 auto 20px auto; 58 | } 59 | 60 | .setting { 61 | display: flex; 62 | font-size: 12px; 63 | font-weight: 400; 64 | position: relative; 65 | flex-wrap: wrap; 66 | justify-content: flex-end; 67 | min-height: 44px; 68 | } 69 | 70 | .setting-description { 71 | margin: auto 0; 72 | } 73 | 74 | .btn { 75 | background-color: rgb(42, 52, 61); 76 | border: 1px solid #3e4852; 77 | border-radius: 3px; 78 | box-sizing: border-box; 79 | display: inline-block; 80 | position: relative; 81 | height: 30px; 82 | cursor: pointer; 83 | } 84 | 85 | hr { 86 | margin-top: 2px; 87 | margin-bottom: 2px; 88 | border: 0; 89 | border-top: 1px solid #3e4852; 90 | box-sizing: content-box; 91 | height: 0; 92 | overflow: visible; 93 | } 94 | 95 | .close { 96 | margin: 6px 3px; 97 | font-family: "Segoe UI", system-ui, sans-serif; 98 | display: inline-block; 99 | vertical-align: middle; 100 | animation: pop 16ms linear; 101 | width: 18px; 102 | height: 18px; 103 | border-radius: 1px; 104 | background-color: rgba(0, 0, 0, 0); 105 | opacity: 1; 106 | border: 0; 107 | padding: 0; 108 | fill: #fafafa; 109 | } 110 | 111 | .close:hover { 112 | fill: red; 113 | background-color: rgba(0, 0, 0, 0.3); 114 | } 115 | 116 | .btn-text { 117 | display: inline-block; 118 | align-items: center; 119 | vertical-align: middle; 120 | margin: 6px 10px; 121 | } 122 | 123 | .hotkeys { 124 | margin: 7px 4px; 125 | } 126 | 127 | .hotkeys .btn-text { 128 | margin-right: 0; 129 | } 130 | 131 | .btn-hov:hover { 132 | background-color: #3e4852; 133 | } 134 | 135 | .btn-enter { 136 | background-color: #00b1e2; 137 | margin: 7px 4px; 138 | } 139 | 140 | .reset-area { 141 | display: flex; 142 | justify-content: center; 143 | position: relative; 144 | margin: 50px 0; 145 | } 146 | 147 | .reset { 148 | font-size: 12px; 149 | font-weight: 400; 150 | background-color: rgb(202, 74, 74); 151 | } 152 | 153 | .reset:hover { 154 | background-color: rgb(148, 54, 54); 155 | } 156 | 157 | .info { 158 | color: #00b1e2; 159 | position: relative; 160 | display: inline-block; 161 | margin-left: 4px; 162 | } 163 | 164 | .info .info-content { 165 | visibility: hidden; 166 | width: max-content; 167 | max-width: 450px; 168 | background-color: rgb(44, 44, 44); 169 | border: 1px solid rgb(1, 140, 179); 170 | color: #fff; 171 | padding: 5px 8px; 172 | border-radius: 6px; 173 | display: inline-block; 174 | font-style: normal; 175 | 176 | /* Positioning the tooltip */ 177 | position: absolute; 178 | z-index: 1; 179 | top: -5px; 180 | left: 105%; 181 | } 182 | 183 | .info-content img { 184 | margin-top: 6px; 185 | max-width: 432px; 186 | } 187 | 188 | /* Show the tooltip when the mouse hovers the tooltip container */ 189 | .info:hover .info-content { 190 | visibility: visible; 191 | } 192 | 193 | .options-container { 194 | margin: 30px 0 15px 0; 195 | width: 100%; 196 | text-align: center; 197 | } 198 | 199 | .options-container input[type="radio"] { 200 | position: absolute; 201 | clip: rect(0, 0, 0, 0); 202 | pointer-events: none; 203 | } 204 | 205 | .options-container img { 206 | height: 200px; 207 | border-radius: 8px; 208 | box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); 209 | } 210 | 211 | .addHotkey { 212 | opacity: 0; 213 | margin: 7px 4px; 214 | transition: opacity 0.4s ease-out; 215 | } 216 | 217 | .setting:hover .addHotkey { 218 | opacity: 1; 219 | transition: opacity 0.2s linear; 220 | } 221 | 222 | .setting > div:first-of-type { 223 | margin: auto 4px auto auto; 224 | } 225 | 226 | /* For Notification Messages */ 227 | 228 | #notify { 229 | --notificationBGColor: rgba(0, 137, 255, 0.6); 230 | --notificationBorder: rgb(0, 137, 255); 231 | --notificationTextColor: white; 232 | position: sticky; 233 | top: 0px; 234 | width: 400px; 235 | height: 0; 236 | color: var(--notificationTextColor); 237 | font-size: 15px; 238 | font-weight: 500; 239 | text-align: center; 240 | background: var(--notificationBGColor); 241 | overflow: hidden; 242 | box-sizing: border-box; 243 | transition: height .2s, border .2s, margin .2s; 244 | left: 50%; 245 | transform: translateX(-50%); 246 | } 247 | 248 | #notifyText { 249 | line-height: 50px; 250 | vertical-align: middle; 251 | } 252 | 253 | .active { 254 | height:50px !important; 255 | border-bottom: 2px var(--notificationBorder) solid !important; 256 | margin-top: -50px; 257 | } 258 | -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | var SETTINGS_FULL; 2 | var HOTKEY_CODES; 3 | 4 | function getSettings(callback) { 5 | chrome.storage.local.get(["extension-settings"], function (result) { 6 | SETTINGS_FULL = result["extension-settings"]; 7 | HOTKEY_CODES = SETTINGS_FULL.hotkeys.codes; 8 | if (callback instanceof Function) { 9 | callback(); 10 | } 11 | }); 12 | } 13 | 14 | function populateFields() { 15 | let hotkeyDivs = document.querySelectorAll(".setting[data-type='shortcut']"); 16 | hotkeyDivs.forEach((hotkeyDiv) => { 17 | setHotkeyBtn(hotkeyDiv); 18 | }); 19 | } 20 | 21 | function enterNewHotkey(event) { 22 | let element = event.currentTarget; 23 | element.removeEventListener("click", enterNewHotkey); 24 | element.setAttribute("class", "btn btn-enter"); 25 | let textArea = element.children[0]; 26 | textArea.innerHTML = ` 27 | Enter the shortcut 28 | `; 29 | 30 | let keysDown = new Set(); 31 | let keysFinal; 32 | window.addEventListener("keydown", keyPress); 33 | window.addEventListener("keyup", keyRelease); 34 | 35 | function keyPress(e) { 36 | e.preventDefault(); 37 | e.stopPropagation(); 38 | 39 | // hitting escape cancels input 40 | if (e.key.toLowerCase() === "escape") { 41 | window.removeEventListener("keydown", keyPress); 42 | window.removeEventListener("keyup", keyRelease); 43 | setHotkeyBtn(element.parentNode); 44 | } 45 | keysDown.add(e.key.toLowerCase(), keysDown.size); 46 | textArea.innerHTML = formateHotkeys(keysDown); 47 | keysFinal = new Set(keysDown); 48 | } 49 | function keyRelease(e) { 50 | keysDown.delete(e.key.toLowerCase()); 51 | 52 | // once no more keys are pressed, the final combo is recorded 53 | if (keysDown.size === 0) { 54 | window.removeEventListener("keydown", keyPress); 55 | window.removeEventListener("keyup", keyRelease); 56 | // setHotkeyBtn(element.parentNode); 57 | let newHotkey = [...keysFinal]; 58 | if (isNotDuplicateHotkey(newHotkey, HOTKEY_CODES)) { 59 | updateHotkey(element, newHotkey); 60 | alertMessage("success", "Hotkey successfully added!", 2000); 61 | } else { 62 | updateHotkey(element, null); 63 | alertMessage("failure", "That hotkey is already in use...", 2000); 64 | } 65 | } 66 | } 67 | } 68 | 69 | function alertMessage(type, text, timeOut) { 70 | let notification = document.getElementById("notify"); 71 | let notificationText = document.getElementById("notifyText"); 72 | 73 | // color the notification 74 | let bgColor, textColor, borderColor; 75 | switch (type) { 76 | case "success": 77 | bgColor = "rgb(67 255 125 / 70%)"; 78 | textColor = "white"; 79 | borderColor = "lime"; 80 | break; 81 | case "failure": 82 | bgColor = "rgb(255 70 104 / 70%)"; 83 | textColor = "white"; 84 | borderColor = "red"; 85 | break; 86 | default: 87 | bgColor = "rgb(0 137 255 / 60%)"; 88 | textColor = "white"; 89 | borderColor = "rgb(0 137 255)"; 90 | break; 91 | } 92 | notification.style.setProperty("--notificationBGColor", bgColor); 93 | notification.style.setProperty("--notificationBorder", borderColor); 94 | notification.style.setProperty("--notificationTextColor", textColor); 95 | 96 | // Set the text 97 | notificationText.innerHTML = text; 98 | 99 | // Show the notification for the specified time 100 | notification.classList.add("active"); 101 | setTimeout(function () { 102 | notification.classList.remove("active"); 103 | }, timeOut); 104 | } 105 | 106 | function updateHotkey(element, newVal, index) { 107 | let parentSection = element.parentNode; 108 | parentSection.removeChild(element); 109 | let newSettings = SETTINGS_FULL; 110 | 111 | if (newVal === null && typeof index !== "undefined") { 112 | // remove the hotkey at the index provided 113 | newSettings.hotkeys.codes[parentSection.id].splice(index, 1); 114 | } else if (newVal !== null) { 115 | // add the new hotkey to the beginning of the stored hotkeys 116 | newSettings.hotkeys.codes[parentSection.id].unshift(newVal); 117 | } 118 | 119 | chrome.storage.local.set({ "extension-settings": newSettings }, function () { 120 | getSettings(setHotkeyBtn(parentSection)); 121 | // TODO: Send message to content scripts that settings updated 122 | }); 123 | } 124 | 125 | function formateHotkeys(set1) { 126 | let replaceTable = { 127 | Control: "Ctrl", 128 | Arrowup: "↑", 129 | Arrowright: "→", 130 | Arrowdown: "↓", 131 | Arrowleft: "←", 132 | " ": " Space", 133 | Pageup: "PgUp", 134 | Pagedown: "PgDn", 135 | Delete: "Del", 136 | }; 137 | 138 | let keyString = [...set1].map((c) => c.slice(0, 1).toUpperCase() + c.slice(1).toLowerCase()).join(" + "); 139 | keyString = keyString.replace( 140 | /Control|Arrowup|Arrowright|Arrowdown|Arrowleft|\s\s|Pageup|Pagedown|Delete/g, 141 | function (match) { 142 | return replaceTable[match]; 143 | } 144 | ); 145 | keyString = keyString === " " ? "Space" : keyString; 146 | 147 | return keyString; 148 | } 149 | 150 | function setHotkeyBtn(btnParent) { 151 | let hotkeys = HOTKEY_CODES[btnParent.id]; 152 | 153 | // remove old buttons 154 | while (btnParent.childNodes.length > 2) { 155 | btnParent.removeChild(btnParent.lastChild); 156 | } 157 | 158 | // TODO: Check if hotkey combo in storage 159 | 160 | let createNewBtn = document.createElement("div"); 161 | createNewBtn.setAttribute("id", btnParent.id + "-btn"); 162 | createNewBtn.setAttribute("class", "btn btn-hov addHotkey"); 163 | createNewBtn.innerHTML = ` 164 | 165 | Click to type a new shortcut 166 | 167 | `; 168 | // Add listner for new hotkey input 169 | createNewBtn.addEventListener("click", enterNewHotkey); 170 | btnParent.appendChild(createNewBtn); 171 | 172 | // if in storage print stored combo 173 | if (hotkeys.length !== 0) { 174 | hotkeys.forEach((hotkey, index) => { 175 | let el = document.createElement("div"); 176 | el.setAttribute("id", btnParent.id + "-hotkey-" + index); 177 | el.setAttribute("class", "btn hotkeys"); 178 | el.innerHTML = ` 179 | 180 | ${formateHotkeys(new Set(hotkey))} 181 | 182 | 187 | `; 188 | el.getElementsByClassName("close")[0].addEventListener( 189 | "click", 190 | (deleteHotkey = function () { 191 | updateHotkey(el, null, index); 192 | alertMessage("success", "Hotkey removed!", 2000); 193 | }) 194 | ); 195 | btnParent.appendChild(el); 196 | }); 197 | } 198 | } 199 | 200 | function isNotDuplicateHotkey(newHotkey, allHotkeys) { 201 | let arrayHotkeys = Object.values(allHotkeys); 202 | for (command of arrayHotkeys) { 203 | for (hotkey of command) { 204 | if (areArraysEqual(newHotkey, hotkey)) { 205 | return false; 206 | } 207 | } 208 | } 209 | // no duplicates found 210 | return true; 211 | } 212 | 213 | function areArraysEqual(a, b) { 214 | if (a === b) return true; 215 | if (a == null || b == null) return false; 216 | if (a.length !== b.length) return false; 217 | 218 | for (var i = 0; i < a.length; ++i) { 219 | if (a[i] !== b[i]) return false; 220 | } 221 | return true; 222 | } 223 | 224 | getSettings(function () { 225 | // once the DOM is ready file in all settings info 226 | let stateCheck = setInterval(() => { 227 | if (document.readyState === "complete") { 228 | clearInterval(stateCheck); 229 | populateFields(); 230 | } 231 | }, 100); 232 | }); 233 | -------------------------------------------------------------------------------- /js/spatial-navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * spatial-navigation 4 | * 5 | * @name spatial-navigation 6 | * @version 0.4.7 7 | * --- 8 | * @author falsandtru https://github.com/falsandtru/spatial-navigation 9 | * @copyright 2015, falsandtru 10 | * @license MIT 11 | * 12 | */ 13 | 14 | !new function(NAME, VERSION) { 15 | "use strict"; 16 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 216 | var LIBRALY; 217 | (function (LIBRALY) { 218 | var UTILITY; 219 | (function (UTILITY) { 220 | var duff = UTILITY.duff; 221 | function repeat(arg, size) { 222 | /* tslint:disable:no-duplicate-variable */ 223 | switch (true) { 224 | case typeof arg === 'string': 225 | var text = arg; 226 | return Array(size + 1).join(text); 227 | case arg instanceof Array: 228 | var len = arg.length; 229 | if (size < 300) { 230 | var arr = Array(size); 231 | duff(-size, function (i) { return arr[i] = arg[i % len]; }); 232 | } 233 | else { 234 | var arr = arg.slice(); 235 | while (arr.length * 2 <= size) { 236 | arr = arr.concat(arr); 237 | } 238 | arr = arr.concat(arr.slice(0, size - arr.length)); 239 | } 240 | return arr; 241 | } 242 | } 243 | UTILITY.repeat = repeat; 244 | })(UTILITY = LIBRALY.UTILITY || (LIBRALY.UTILITY = {})); 245 | })(LIBRALY || (LIBRALY = {})); 246 | /// 247 | var LIBRALY; 248 | (function (LIBRALY) { 249 | var TYPE; 250 | (function (TYPE) { 251 | var REPEAT = LIBRALY.UTILITY.repeat; 252 | TYPE.id = (function () { 253 | var id = {}; 254 | return { 255 | rest: function () { return id; } 256 | }; 257 | })(); 258 | TYPE.type = (function () { 259 | var TYPES = {}, toString = TYPES.toString; 260 | 'Boolean Number String Function Array Date RegExp Object Error'.split(/\s+/) 261 | .forEach(function (v, i) { return TYPES['[object ' + v + ']'] = v.toLowerCase(); }); 262 | return type; 263 | function type(target, pattern, cut, depth) { 264 | switch (typeof pattern) { 265 | case 'undefined': 266 | if (target === null) { 267 | return null + ''; 268 | } 269 | var tgttypeof = typeof target; 270 | switch (tgttypeof) { 271 | case 'object': 272 | return target instanceof Array 273 | ? 'array' 274 | : TYPES[toString.call(target)] || 'object'; 275 | case 'function': 276 | switch (target) { 277 | case Boolean: 278 | case Number: 279 | case String: 280 | case Function: 281 | case Array: 282 | case Date: 283 | case RegExp: 284 | case Object: 285 | case Error: 286 | return 'function'; 287 | } 288 | return TYPES[toString.call(target)] || 'object'; 289 | } 290 | return tgttypeof; 291 | case 'string': 292 | var tgttype = type(target); 293 | return pattern === tgttype || pattern.length > tgttype.length + 5 && pattern.indexOf(tgttype) > -1; 294 | default: 295 | return compare(target, pattern, cut, depth); 296 | } 297 | } 298 | })(); 299 | var compare = (function () { 300 | var MAX_DEPTH = 9; 301 | var rfname = /function\s*([^(]*)/; 302 | return pattern; 303 | function pattern(args, patterns, cut, depth, rest, acc) { 304 | if (cut === void 0) { cut = 0; } 305 | if (depth === void 0) { depth = MAX_DEPTH; } 306 | if (rest === void 0) { rest = false; } 307 | if (acc === void 0) { acc = []; } 308 | if (0 > depth) { 309 | return false; 310 | } 311 | if (patterns.length > 1 && patterns[patterns.length - 1] === TYPE.id.rest()) { 312 | return compare(args, clone(patterns, 0, -1), cut, depth, true, acc); 313 | } 314 | if (acc.length === 0) { 315 | // 末尾のundefinedを削除 316 | args = compact(args, patterns); 317 | } 318 | if (rest && patterns.length < args.length) { 319 | patterns = expand(args, patterns); 320 | } 321 | if (args.length !== patterns.length) { 322 | return false; 323 | } 324 | for (var i = args.length; i--;) { 325 | var arg = args[i], pat = patterns[i]; 326 | switch (TYPE.type(pat)) { 327 | case 'undefined': 328 | // すべてに一致 329 | continue; 330 | case 'null': 331 | // nullまたはundefined 332 | if (null === arg || void 0 === arg) { 333 | continue; 334 | } 335 | return false; 336 | case 'boolean': 337 | case 'string': 338 | // 値を比較 339 | if (pat === arg) { 340 | continue; 341 | } 342 | return false; 343 | case 'number': 344 | // 値を比較 345 | if (pat === arg || TYPE.type(arg, 'number') && isNaN(arg) && isNaN(pat)) { 346 | continue; 347 | } 348 | return false; 349 | case 'date': 350 | // 値を比較 351 | if (TYPE.type(arg, 'date') && arg.getUTCMilliseconds() === pat.getUTCMilliseconds()) { 352 | continue; 353 | } 354 | return false; 355 | case 'array': 356 | // パターン値が1要素のときは1要素以上の配列としてパターンマッチ 357 | // それ以外の場合はタプルとしてパターンマッチ 358 | if (acc.length && ~acc.indexOf(arg)) { 359 | if (arg === pat) { 360 | continue; 361 | } 362 | else { 363 | return false; 364 | } 365 | } 366 | else if (arg === pat 367 | || TYPE.type(arg, 'array') && compareArray(arg, pat, cut, depth - 1, acc.length === 0 ? [arg] : acc.concat([arg]))) { 368 | continue; 369 | } 370 | return false; 371 | case 'object': 372 | // 列挙可能な値を比較 373 | if (acc.length && ~acc.indexOf(arg)) { 374 | if (arg === pat) { 375 | continue; 376 | } 377 | else { 378 | return false; 379 | } 380 | } 381 | else if (arg === pat 382 | || TYPE.type(arg, 'object') && compareObject(arg, pat, cut, depth - 1, acc.length === 0 ? [arg] : acc.concat([arg]))) { 383 | continue; 384 | } 385 | return false; 386 | case 'function': 387 | // 型または値を比較 388 | if (arg === void 0 || arg === null || TYPE.type(arg, 'number') && isNaN(arg)) { 389 | return false; 390 | } 391 | switch (pat) { 392 | case Boolean: 393 | if (TYPE.type(arg, 'boolean')) { 394 | continue; 395 | } 396 | return false; 397 | case Number: 398 | if (TYPE.type(arg, 'number')) { 399 | continue; 400 | } 401 | return false; 402 | case String: 403 | if (TYPE.type(arg, 'string')) { 404 | continue; 405 | } 406 | return false; 407 | case Date: 408 | if (TYPE.type(arg, 'date')) { 409 | continue; 410 | } 411 | return false; 412 | case Array: 413 | if (TYPE.type(arg, 'array')) { 414 | continue; 415 | } 416 | return false; 417 | case Object: 418 | if (TYPE.type(arg, 'object')) { 419 | continue; 420 | } 421 | return false; 422 | case RegExp: 423 | if (TYPE.type(arg, 'regexp')) { 424 | continue; 425 | } 426 | return false; 427 | case Function: 428 | if (TYPE.type(arg, 'function')) { 429 | continue; 430 | } 431 | return false; 432 | default: 433 | if (arg instanceof pat) { 434 | continue; 435 | } 436 | return false; 437 | } 438 | case 'error': 439 | // 型とメッセージを比較 440 | if (void 0 !== arg && null !== arg && pat.constructor === arg.constructor && pat.name === arg.name && pat.message === arg.message) { 441 | continue; 442 | } 443 | return false; 444 | case 'regexp': 445 | // 値を比較 446 | if (void 0 !== arg && null !== arg && pat.constructor === arg.constructor && pat.source === arg.source && 447 | pat.global === arg.global && pat.ignoreCase === arg.ignoreCase && pat.multiline === arg.multiline) { 448 | continue; 449 | } 450 | return false; 451 | default: 452 | throw new Error('Undefined pattern.'); 453 | } 454 | } 455 | return true; 456 | } 457 | function clone(arr, begin, end) { 458 | if (begin === void 0) { begin = 0; } 459 | if (end === void 0) { end = Infinity; } 460 | if (end < 0) { 461 | return clone(arr, begin, arr.length + end); 462 | } 463 | if (begin < 0) { 464 | return clone(arr, arr.length + begin, end); 465 | } 466 | var args_ = []; 467 | for (var _i = end > arr.length ? arr.length - 1 : end - 1; _i >= begin; _i--) { 468 | args_[_i - begin] = arr[_i]; 469 | } 470 | return args_; 471 | } 472 | function compact(args, patterns) { 473 | if (args.length <= patterns.length) { 474 | return args; 475 | } 476 | for (var i = args.length; i-- && i + 1 > patterns.length && void 0 === args[i];) { 477 | continue; 478 | } 479 | return i + 1 === args.length ? args : clone(args, 0, i + 1); 480 | } 481 | function expand(args, patterns) { 482 | if (args.length <= patterns.length) { 483 | return patterns; 484 | } 485 | return patterns.concat(REPEAT(clone(patterns, -1), args.length - patterns.length)); 486 | } 487 | function isConstructor(F) { 488 | /* tslint:disable:no-string-literal */ 489 | var fname = typeof F['name'] === 'string' ? F['name'] : F.toString().match(rfname).pop() + ' '; 490 | var fst = fname.charAt(0), snd = fname.charAt(1); 491 | return 'A' <= fst && fst <= 'Z' && !('A' <= snd && snd <= 'Z'); 492 | } 493 | function compareArray(arg, pattern, cut, depth, acc) { 494 | if (arg.length < pattern.length - 1) { 495 | return false; 496 | } 497 | if (pattern.length === 1 && cut > 0 && arg.length > cut * 2) { 498 | arg = clone(arg, 0, cut).concat(clone(arg, -cut)); 499 | } 500 | return compare(arg, pattern, cut, depth, pattern.length === 1, acc); 501 | } 502 | function compareObject(arg, pattern, cut, depth, acc) { 503 | /* tslint:disable:forin */ 504 | var empty = true; 505 | for (var i in pattern) { 506 | empty = false; 507 | if (!(i in arg) || !compare([arg[i]], [pattern[i]], cut, depth, false, acc)) { 508 | return false; 509 | } 510 | } 511 | if (!empty) { 512 | return true; 513 | } 514 | else { 515 | for (var i in arg) { 516 | return false; 517 | } 518 | return true; 519 | } 520 | } 521 | })(); 522 | })(TYPE = LIBRALY.TYPE || (LIBRALY.TYPE = {})); 523 | })(LIBRALY || (LIBRALY = {})); 524 | /// 525 | /// 526 | var LIBRALY; 527 | (function (LIBRALY) { 528 | var PROMISE; 529 | (function (PROMISE) { 530 | var APPLY = LIBRALY.FUNCTION.apply; 531 | var TYPE = LIBRALY.TYPE.type; 532 | function isThenable(target) { 533 | return TYPE(target, 'object') && 'then' in target; 534 | } 535 | PROMISE.isThenable = isThenable; 536 | PROMISE.resolve = typeof Promise === 'function' ? Promise.resolve.bind(Promise) 537 | : function (value) { return PROMISE.deferred().resolve(value); }; 538 | // ネイティブメソッドと異なり同期処理されなくともエラーとならず非同期に伝達される 539 | PROMISE.reject = function (value) { return PROMISE.deferred().reject(value); }; 540 | // notifyサポートのためネイティブメソッドは使用しない 541 | PROMISE.when = function (promises) { 542 | if (promises.length === 0) { 543 | return PROMISE.deferred().resolve([]); 544 | } 545 | var dfd = PROMISE.deferred(), count = promises.length, waits = Array(count); 546 | for (var i = 0, len = promises.length; i < len; i++) { 547 | register(i, promises[i]); 548 | } 549 | return dfd.promise(); 550 | function register(index, arg) { 551 | if (isThenable(arg)) { 552 | arg 553 | .then(function () { 554 | var args = []; 555 | for (var _i = 0; _i < arguments.length; _i++) { 556 | args[_i - 0] = arguments[_i]; 557 | } 558 | return done(index, args); 559 | }, dfd.reject, dfd.notify); 560 | } 561 | else { 562 | done(index, [arg]); 563 | } 564 | } 565 | function done(index, args) { 566 | if (count === 0 || dfd.state() !== 'pending') { 567 | return; 568 | } 569 | count -= 1; 570 | waits[index] = args; 571 | if (count === 0) { 572 | APPLY(dfd.resolve, void 0, waits); 573 | } 574 | } 575 | }; 576 | PROMISE.deferred = (function () { 577 | return function (callback) { 578 | var statePending = 'pending', stateResolved = 'resolved', stateRejected = 'rejected', state = statePending, memoryNotify, memoryResolve, memoryReject, memoryAlways, listenerProgressCallbacks = [], listenerDoneCallbacks = [], listenerFailCallbacks = [], listenerAlwaysCallbacks = [], dfd = { 579 | state: function () { 580 | return state; 581 | }, 582 | progress: function () { 583 | var args = []; 584 | for (var _i = 0; _i < arguments.length; _i++) { 585 | args[_i - 0] = arguments[_i]; 586 | } 587 | if (state === statePending) { 588 | registerWithCall(memoryNotify, flatten(args), listenerProgressCallbacks); 589 | } 590 | return this; 591 | }, 592 | done: function () { 593 | var args = []; 594 | for (var _i = 0; _i < arguments.length; _i++) { 595 | args[_i - 0] = arguments[_i]; 596 | } 597 | if (state === statePending || state === stateResolved) { 598 | registerWithCall(memoryResolve, flatten(args), listenerDoneCallbacks); 599 | } 600 | return this; 601 | }, 602 | fail: function () { 603 | var args = []; 604 | for (var _i = 0; _i < arguments.length; _i++) { 605 | args[_i - 0] = arguments[_i]; 606 | } 607 | if (state === statePending || state === stateRejected) { 608 | registerWithCall(memoryReject, flatten(args), listenerFailCallbacks); 609 | } 610 | return this; 611 | }, 612 | always: function () { 613 | var args = []; 614 | for (var _i = 0; _i < arguments.length; _i++) { 615 | args[_i - 0] = arguments[_i]; 616 | } 617 | registerWithCall(memoryAlways, flatten(args), listenerAlwaysCallbacks); 618 | return this; 619 | }, 620 | then: function (doneCallbacks, failCallbacks, progressCallbacks) { 621 | var nextDfd = PROMISE.deferred(); 622 | // progress 623 | if (state === statePending) { 624 | cascade(memoryNotify, progressCallbacks, listenerProgressCallbacks, nextDfd.notify); 625 | } 626 | // done 627 | if (state === statePending || state === stateResolved) { 628 | cascade(memoryResolve, doneCallbacks, listenerDoneCallbacks, nextDfd.resolve); 629 | } 630 | // fail 631 | if (state === statePending || state === stateRejected) { 632 | cascade(memoryReject, failCallbacks, listenerFailCallbacks, nextDfd.reject); 633 | } 634 | return nextDfd.promise(); 635 | function cascade(memory, callbacks, listenerCallbacks, callback) { 636 | callbacks && 637 | registerWithCall(memory, [function () { 638 | var args = []; 639 | for (var _i = 0; _i < arguments.length; _i++) { 640 | args[_i - 0] = arguments[_i]; 641 | } 642 | var result; 643 | if (typeof callbacks === 'function') { 644 | result = APPLY(callbacks, void 0, args); 645 | if (TYPE(result, 'object') && TYPE(result.then, 'function')) { 646 | result 647 | .then(nextDfd.notify, nextDfd.notify, nextDfd.notify); 648 | } 649 | else { 650 | callback(result); 651 | } 652 | } 653 | else { 654 | callbacks 655 | .forEach(function (f) { return APPLY(f, void 0, args); }); 656 | callback(); 657 | } 658 | }], listenerCallbacks); 659 | } 660 | }, 661 | promise: function () { 662 | return { 663 | state: dfd.state, 664 | progress: dfd.progress, 665 | done: dfd.done, 666 | fail: dfd.fail, 667 | always: dfd.always, 668 | then: dfd.then 669 | }; 670 | }, 671 | notify: function () { 672 | var args = []; 673 | for (var _i = 0; _i < arguments.length; _i++) { 674 | args[_i - 0] = arguments[_i]; 675 | } 676 | if (state === statePending) { 677 | memoryNotify = args; 678 | call(memoryNotify, listenerProgressCallbacks); 679 | } 680 | return this; 681 | }, 682 | resolve: function () { 683 | var args = []; 684 | for (var _i = 0; _i < arguments.length; _i++) { 685 | args[_i - 0] = arguments[_i]; 686 | } 687 | if (state === statePending) { 688 | memoryResolve = args; 689 | memoryAlways = args; 690 | call(args, listenerDoneCallbacks.concat(listenerAlwaysCallbacks)); 691 | listenerProgressCallbacks = []; 692 | listenerDoneCallbacks = []; 693 | listenerAlwaysCallbacks = []; 694 | state = stateResolved; 695 | } 696 | return this; 697 | }, 698 | reject: function () { 699 | var args = []; 700 | for (var _i = 0; _i < arguments.length; _i++) { 701 | args[_i - 0] = arguments[_i]; 702 | } 703 | if (state === statePending) { 704 | memoryReject = args; 705 | memoryAlways = args; 706 | call(args, listenerFailCallbacks.concat(listenerAlwaysCallbacks)); 707 | listenerProgressCallbacks = []; 708 | listenerFailCallbacks = []; 709 | listenerAlwaysCallbacks = []; 710 | state = stateRejected; 711 | } 712 | return this; 713 | } 714 | }; 715 | if (callback) { 716 | callback(dfd.resolve, dfd.reject, dfd.notify); 717 | } 718 | return dfd; 719 | function registerWithCall(memory, callbacks, listenerCallbacks) { 720 | var result; 721 | for (var i = 0, len = callbacks.length; i < len; i++) { 722 | var callback = callbacks[i]; 723 | listenerCallbacks.push(callback); 724 | result = call(memory, [callback]); 725 | } 726 | if (state !== statePending) { 727 | listenerCallbacks.splice(0, listenerCallbacks.length); 728 | } 729 | return result; 730 | } 731 | function call(memory, callbacks) { 732 | var result; 733 | for (var i = 0, len = memory ? callbacks.length : 0; i < len; i++) { 734 | result = APPLY(callbacks[i], void 0, memory); 735 | } 736 | return result; 737 | } 738 | function flatten(arr) { 739 | var acc = []; 740 | if (arr instanceof Array) { 741 | for (var i = arr.length; i--;) { 742 | var elem = arr[i]; 743 | if (elem instanceof Array) { 744 | acc = acc.concat(flatten(elem)); 745 | } 746 | else { 747 | acc.push(elem); 748 | } 749 | } 750 | } 751 | return acc; 752 | } 753 | }; 754 | })(); 755 | })(PROMISE = LIBRALY.PROMISE || (LIBRALY.PROMISE = {})); 756 | })(LIBRALY || (LIBRALY = {})); 757 | /// 758 | var LIBRALY; 759 | (function (LIBRALY) { 760 | var FUNCTION; 761 | (function (FUNCTION) { 762 | FUNCTION.memoize = function (func) { 763 | var noarg; 764 | return function () { 765 | if (noarg === void 0) { 766 | noarg = func(); 767 | } 768 | return noarg; 769 | }; 770 | }; 771 | })(FUNCTION = LIBRALY.FUNCTION || (LIBRALY.FUNCTION = {})); 772 | })(LIBRALY || (LIBRALY = {})); 773 | var LIBRALY; 774 | (function (LIBRALY) { 775 | var UUID; 776 | (function (UUID) { 777 | UUID.v4 = function () { 778 | // version 4 779 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 780 | var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); 781 | return v.toString(16).toUpperCase(); 782 | }); 783 | }; 784 | })(UUID = LIBRALY.UUID || (LIBRALY.UUID = {})); 785 | })(LIBRALY || (LIBRALY = {})); 786 | /// 787 | /// 788 | /// 789 | /// 790 | /// 791 | /// 792 | /// 793 | var undefined; 794 | var MODULE; 795 | (function (MODULE) { 796 | MODULE.APPLY = LIBRALY.FUNCTION.apply; 797 | MODULE.TYPE = LIBRALY.TYPE.type; 798 | MODULE.PROMISE = LIBRALY.PROMISE; 799 | MODULE.MEMOIZE = LIBRALY.FUNCTION.memoize; 800 | MODULE.UUID = LIBRALY.UUID.v4; 801 | var ID; 802 | (function (ID) { 803 | ID.rest = LIBRALY.TYPE.id.rest; 804 | })(ID = MODULE.ID || (MODULE.ID = {})); 805 | ; 806 | })(MODULE || (MODULE = {})); 807 | /// 808 | /// 809 | /// 810 | /* CONTROLLER */ 811 | var MODULE; 812 | (function (MODULE) { 813 | var CONTROLLER; 814 | (function (CONTROLLER) { 815 | var Stream = (function () { 816 | function Stream(message, extensions, parent) { 817 | var _this = this; 818 | if (extensions === void 0) { extensions = []; } 819 | if (parent === void 0) { parent = null; } 820 | this.NAME = NAME; 821 | this.VERSION = VERSION; 822 | this.__LazyChain__ = { 823 | extensions: null, 824 | parent: null, 825 | child: null, 826 | deferred: null, 827 | command: null, 828 | monad: null, 829 | transfer: function (method, args) { 830 | _this.__LazyChain__.command = [method, args]; 831 | return _this.__LazyChain__.child = MODULE.APPLY(CONTROLLER[method], void 0, [_this].concat(args)); 832 | } 833 | }; 834 | this.lazy = (function () { 835 | var self = _this; 836 | return function () { 837 | var args = []; 838 | for (var _i = 0; _i < arguments.length; _i++) { 839 | args[_i - 0] = arguments[_i]; 840 | } 841 | return self.__LazyChain__.transfer('lazy', args); 842 | }; 843 | })(); 844 | this.stream = (function () { 845 | var self = _this; 846 | return function () { 847 | var args = []; 848 | for (var _i = 0; _i < arguments.length; _i++) { 849 | args[_i - 0] = arguments[_i]; 850 | } 851 | return self.__LazyChain__.transfer('stream', args); 852 | }; 853 | })(); 854 | this.pattern = (function () { 855 | var self = _this; 856 | return function () { 857 | var args = []; 858 | for (var _i = 0; _i < arguments.length; _i++) { 859 | args[_i - 0] = arguments[_i]; 860 | } 861 | return self.__LazyChain__.transfer('pattern', args); 862 | }; 863 | })(); 864 | this.monad = (function () { 865 | var self = _this; 866 | return function () { 867 | var args = []; 868 | for (var _i = 0; _i < arguments.length; _i++) { 869 | args[_i - 0] = arguments[_i]; 870 | } 871 | return self.__LazyChain__.transfer('monad', args); 872 | }; 873 | })(); 874 | this.monadic = (function () { 875 | var self = _this; 876 | return function () { 877 | var args = []; 878 | for (var _i = 0; _i < arguments.length; _i++) { 879 | args[_i - 0] = arguments[_i]; 880 | } 881 | return self.__LazyChain__.transfer('monadic', args); 882 | }; 883 | })(); 884 | this.array = (function () { 885 | var self = _this; 886 | return function () { 887 | var result; 888 | self.some(function (v, i, a) { return result = a; }); 889 | return result || []; 890 | }; 891 | })(); 892 | this.prevChain = (function () { 893 | var self = _this; 894 | return function () { 895 | return self.__LazyChain__.parent; 896 | }; 897 | })(); 898 | this.nextChain = (function () { 899 | var self = _this; 900 | return function () { 901 | return self.__LazyChain__.child; 902 | }; 903 | })(); 904 | this.firstChain = (function () { 905 | var self = _this; 906 | return function () { 907 | return self.prevChain() && self.prevChain().firstChain() || self; 908 | }; 909 | })(); 910 | this.lastChain = (function () { 911 | var self = _this; 912 | return function () { 913 | return self.nextChain() && self.nextChain().lastChain() || self; 914 | }; 915 | })(); 916 | if (parent) { 917 | parent.__LazyChain__.child = this; 918 | this.__LazyChain__.monad = parent.__LazyChain__.monad; 919 | this.__LazyChain__.extensions = parent.__LazyChain__.extensions; 920 | } 921 | this.__LazyChain__.parent = parent; 922 | Stream.map || 923 | CONTROLLER.extendStream_(true, Array.prototype, 0, [ 924 | 'reverse', 925 | 'slice', 926 | 'sort', 927 | 'every', 928 | 'some', 929 | 'forEach', 930 | 'filter', 931 | 'map', 932 | 'reduce', 933 | 'reduceRight' 934 | ]); 935 | CONTROLLER.mixin(this, this.__LazyChain__.deferred = MODULE.PROMISE.deferred()); 936 | this.__LazyChain__.extensions = this.__LazyChain__.extensions || extensions; 937 | this.__LazyChain__.extensions 938 | .forEach(function (v) { return CONTROLLER.emulate(_this, v); }); 939 | var deferred = this.__LazyChain__.deferred; 940 | if (message instanceof Array) { 941 | message.length && MODULE.APPLY(deferred.notify, void 0, message); 942 | } 943 | else if (message instanceof Stream) { 944 | message 945 | .progress(deferred.notify) 946 | .done(deferred.resolve) 947 | .fail(deferred.reject); 948 | } 949 | else if (MODULE.PROMISE.isThenable(message)) { 950 | message 951 | .then(deferred.notify, deferred.notify, deferred.notify); 952 | } 953 | } 954 | return Stream; 955 | })(); 956 | CONTROLLER.Stream = Stream; 957 | })(CONTROLLER = MODULE.CONTROLLER || (MODULE.CONTROLLER = {})); 958 | })(MODULE || (MODULE = {})); 959 | /// 960 | /// 961 | /* MODEL */ 962 | var MODULE; 963 | (function (MODULE) { 964 | var MODEL; 965 | (function (MODEL) { 966 | var Stream = MODULE.CONTROLLER.Stream; 967 | function connect(inbound, outbound) { 968 | if ('progress' in inbound && 'always' in inbound && 'notify' in outbound) { 969 | inbound 970 | .progress(outbound.notify) 971 | .always(outbound.resolve); 972 | } 973 | else { 974 | inbound.then(outbound.resolve, outbound.reject); 975 | } 976 | return outbound; 977 | } 978 | function cascade(stream, message) { 979 | if (message === void 0) { message = stream; } 980 | return new Stream(message, stream.__LazyChain__.extensions, stream); 981 | } 982 | MODEL.cascade = cascade; 983 | function clone(source, target) { 984 | if (!source.nextChain()) { 985 | return target; 986 | } 987 | target = target || new Stream(null, source.__LazyChain__.extensions); 988 | return clone(source.nextChain(), MODULE.APPLY(target[source.__LazyChain__.command[0]], target, source.__LazyChain__.command[1])); 989 | } 990 | function environment(env) { 991 | if (!MODULE.TYPE(env, 'object')) { 992 | return env; 993 | } 994 | return Object.create(env); 995 | } 996 | function immediate(inbound) { 997 | var reserve = typeof setImmediate === 'function' ? setImmediate : function (f) { return setTimeout(f, 1); }; 998 | return inbound 999 | .lazy(function (_, i, a) { 1000 | if (i > 0) { 1001 | return; 1002 | } 1003 | var dfd = LazyChain.deferred(); 1004 | reserve(function (_) { return MODULE.APPLY(dfd.resolve, void 0, a); }); 1005 | return dfd; 1006 | }); 1007 | } 1008 | MODEL.immediate = immediate; 1009 | function lazy(inbound, callback, env, sequential) { 1010 | if (typeof env === 'boolean' && sequential === void 0) { 1011 | sequential = env; 1012 | env = {}; 1013 | } 1014 | env = environment(env); 1015 | var outbound = cascade(inbound, null); 1016 | var gendefer = callback.length === 5; 1017 | callback && inbound.progress(proxy).always(proxy); 1018 | return outbound; 1019 | function proxy() { 1020 | var args = []; 1021 | for (var _i = 0; _i < arguments.length; _i++) { 1022 | args[_i - 0] = arguments[_i]; 1023 | } 1024 | return args.reduce(conv, []); 1025 | } 1026 | function conv(r, v, i, a) { 1027 | var deferred; 1028 | var result = gendefer ? callback(v, i, a, env, deferred = MODULE.PROMISE.deferred()) : callback(v, i, a, env); 1029 | deferred = MODULE.PROMISE.isThenable(result) ? result : deferred; 1030 | if (!deferred) { 1031 | return r; 1032 | } 1033 | else if (sequential) { 1034 | r.push(deferred); 1035 | if (i + 1 === a.length) { 1036 | MODULE.PROMISE.when(r) 1037 | .then(function () { 1038 | var args = []; 1039 | for (var _i = 0; _i < arguments.length; _i++) { 1040 | args[_i - 0] = arguments[_i]; 1041 | } 1042 | return MODULE.APPLY(outbound.notify, void 0, args.reduce(function (r, v) { return r.concat(v); }, [])); 1043 | }, outbound.notify, outbound.notify); 1044 | } 1045 | } 1046 | else { 1047 | if ('progress' in deferred && 'always' in deferred) { 1048 | deferred.progress(outbound.notify).always(outbound.notify); 1049 | } 1050 | else { 1051 | deferred.then(outbound.notify, outbound.notify, outbound.notify); 1052 | } 1053 | } 1054 | return r; 1055 | } 1056 | } 1057 | MODEL.lazy = lazy; 1058 | function buffer(inbound, clearance) { 1059 | var scope = cascade(inbound), env = { 1060 | prototype: true, 1061 | buffer: [], 1062 | sentinel: {}, 1063 | timer: 0, 1064 | last: 0 1065 | }, delay = clearance < 0; 1066 | clearance = Math.abs(clearance); 1067 | return scope.lazy(function (v, i, a, e) { 1068 | if (!isSentinel(v)) { 1069 | e.buffer.push(v); 1070 | } 1071 | if (e.timer === 0) { 1072 | e.timer = setTimeout(fire, clearance); 1073 | e.last = Date.now(); 1074 | } 1075 | else if (delay && !isSentinel(v)) { 1076 | clearTimeout(e.timer); 1077 | e.timer = setTimeout(fire, clearance); 1078 | } 1079 | else if (isElapsed(e.last) || isSentinel(v)) { 1080 | clearTimeout(e.timer); 1081 | e.timer = 0; 1082 | e.last = Date.now(); 1083 | var dfd = LazyChain.deferred(); 1084 | return MODULE.APPLY(dfd.resolve, dfd, e.buffer.splice(0, e.buffer.length)); 1085 | } 1086 | }, env); 1087 | function fire() { 1088 | return scope.notify(env.sentinel); 1089 | } 1090 | function isElapsed(time) { 1091 | return Date.now() - time > clearance; 1092 | } 1093 | function isSentinel(value) { 1094 | return value === env.sentinel; 1095 | } 1096 | } 1097 | MODEL.buffer = buffer; 1098 | function stream(inbound, callback, env) { 1099 | if (env === void 0) { env = {}; } 1100 | env = environment(env); 1101 | callback && inbound.progress(proxy).always(proxy); 1102 | return cascade(inbound); 1103 | function proxy() { 1104 | var args = []; 1105 | for (var _i = 0; _i < arguments.length; _i++) { 1106 | args[_i - 0] = arguments[_i]; 1107 | } 1108 | return args.forEach(function (v, i, a) { return callback(v, i, a, env); }); 1109 | } 1110 | } 1111 | MODEL.stream = stream; 1112 | function branch(inbound, branches, cascade) { 1113 | if (cascade === void 0) { cascade = true; } 1114 | branches 1115 | .filter(function (branch) { return MODULE.PROMISE.isThenable(branch); }) 1116 | .forEach(function (branch) { return connect(inbound, branch); }); 1117 | return cascade ? MODEL.cascade(inbound) : inbound; 1118 | } 1119 | MODEL.branch = branch; 1120 | function merge(outbound, branches, cascade) { 1121 | if (cascade === void 0) { cascade = true; } 1122 | branches 1123 | .filter(function (branch) { return MODULE.PROMISE.isThenable(branch); }) 1124 | .forEach(function (branch) { return connect(branch, outbound); }); 1125 | return cascade ? MODEL.cascade(outbound) : outbound; 1126 | } 1127 | MODEL.merge = merge; 1128 | function compose(inbound, streams) { 1129 | return streams.reduceRight(function (r, v) { return merge(clone(v.firstChain()).firstChain(), [r], false).lastChain(); }, inbound); 1130 | } 1131 | MODEL.compose = compose; 1132 | function split(inbound, splitter, branches, env) { 1133 | if (env === void 0) { env = {}; } 1134 | env = environment(env); 1135 | var outbound = cascade(inbound, null); 1136 | var scope = cascade(inbound); 1137 | branches = format(branches, outbound); 1138 | scope 1139 | .reduce(function (r, v, i, a) { 1140 | r = i ? r : Object.create(null); 1141 | var index = splitter.call(void 0, v, i, a, env); 1142 | index = 'boolean' === typeof index ? +!index : index; 1143 | if (!(index in r)) { 1144 | r[index] = []; 1145 | } 1146 | r[index].push(v); 1147 | return r; 1148 | }, null) 1149 | .forEach(function (params) { 1150 | Object.keys(params) 1151 | .filter(function (index) { return !!params[index].length; }) 1152 | .forEach(function (index) { 1153 | var action = index in branches ? branches[index] || (function (_) { return void 0; }) : null; 1154 | if (typeof action === 'function') { 1155 | MODULE.APPLY(action, void 0, params[index]); 1156 | } 1157 | else { 1158 | MODULE.APPLY(action.notify, void 0, params[index]); 1159 | } 1160 | }); 1161 | }); 1162 | return outbound; 1163 | function format(branches, outbound) { 1164 | switch (true) { 1165 | case !branches: 1166 | return [outbound, null]; 1167 | case MODULE.PROMISE.isThenable(branches): 1168 | return [outbound, branches]; 1169 | case branches instanceof Array && 1 === branches.length && !MODULE.PROMISE.isThenable(branches[0]): 1170 | branches[0][''] = outbound; 1171 | return branches[0]; 1172 | case branches instanceof Array: 1173 | branches.unshift(outbound); 1174 | branches[1] = branches[1] || null; 1175 | return branches; 1176 | } 1177 | } 1178 | } 1179 | MODEL.split = split; 1180 | function pattern(inbound, patterns) { 1181 | var outbound = cascade(inbound, null); 1182 | inbound 1183 | .progress(function () { 1184 | var params = []; 1185 | for (var _i = 0; _i < arguments.length; _i++) { 1186 | params[_i - 0] = arguments[_i]; 1187 | } 1188 | return MODULE.APPLY(outbound.notify, void 0, conv(params)); 1189 | }) 1190 | .done(function () { 1191 | var params = []; 1192 | for (var _i = 0; _i < arguments.length; _i++) { 1193 | params[_i - 0] = arguments[_i]; 1194 | } 1195 | return MODULE.APPLY(outbound.resolve, void 0, conv(params)); 1196 | }) 1197 | .fail(function () { 1198 | var params = []; 1199 | for (var _i = 0; _i < arguments.length; _i++) { 1200 | params[_i - 0] = arguments[_i]; 1201 | } 1202 | return MODULE.APPLY(outbound.reject, void 0, conv(params)); 1203 | }); 1204 | return outbound; 1205 | function conv(params) { 1206 | return params 1207 | .reduce(function (r, param, index) { 1208 | var match; 1209 | for (var i = 0, len = patterns.length; i < len; i++) { 1210 | var exp = patterns[i], etype = exp.length; 1211 | match = i > 0 && patterns[i][0] === patterns[i - 1][0] ? match : MODULE.TYPE([param], [exp[0]], 1); 1212 | if (!match) { 1213 | continue; 1214 | } 1215 | else if (etype === 1) { 1216 | r[r.length] = param; 1217 | return r; 1218 | } 1219 | else if (etype === 2) { 1220 | r[r.length] = exp[1](param); 1221 | return r; 1222 | } 1223 | else if (etype === 3 && exp[1](param)) { 1224 | r[r.length] = exp[2](param); 1225 | return r; 1226 | } 1227 | } 1228 | return r; 1229 | }, []); 1230 | } 1231 | } 1232 | MODEL.pattern = pattern; 1233 | function castStream2Monad(s) { 1234 | return s; 1235 | } 1236 | function castMonad2Stream(m) { 1237 | return m; 1238 | } 1239 | function monad(inbound, monad, convert) { 1240 | if (convert === void 0) { convert = true; } 1241 | inbound.__LazyChain__.monad = monad; 1242 | switch (convert) { 1243 | case true: 1244 | return castStream2Monad(cascade(inbound.map(function (v) { return monad.return.call(void 0, v); }))); 1245 | case false: 1246 | return castStream2Monad(cascade(inbound)); 1247 | default: 1248 | return castStream2Monad(cascade(inbound.map(function (v) { return MODULE.TYPE([v], [convert], 1) ? monad.return.call(void 0, v) : v; }))); 1249 | } 1250 | } 1251 | MODEL.monad = monad; 1252 | function monadic(inbound, patterns) { 1253 | var outbound = castStream2Monad(cascade(castMonad2Stream(inbound), null)); 1254 | castMonad2Stream(inbound) 1255 | .progress(function () { 1256 | var params = []; 1257 | for (var _i = 0; _i < arguments.length; _i++) { 1258 | params[_i - 0] = arguments[_i]; 1259 | } 1260 | return MODULE.APPLY(outbound.notify, void 0, conv(params)); 1261 | }) 1262 | .done(function () { 1263 | var params = []; 1264 | for (var _i = 0; _i < arguments.length; _i++) { 1265 | params[_i - 0] = arguments[_i]; 1266 | } 1267 | return MODULE.APPLY(outbound.resolve, void 0, conv(params)); 1268 | }) 1269 | .fail(function () { 1270 | var params = []; 1271 | for (var _i = 0; _i < arguments.length; _i++) { 1272 | params[_i - 0] = arguments[_i]; 1273 | } 1274 | return MODULE.APPLY(outbound.reject, void 0, conv(params)); 1275 | }); 1276 | return outbound; 1277 | function conv(params) { 1278 | return params 1279 | .reduce(function (r, param, index) { 1280 | var match; 1281 | for (var i = 0, len = patterns.length; i < len; i++) { 1282 | var exp = patterns[i], etype = exp.length; 1283 | match = i > 0 && patterns[i - 1][0] === patterns[i][0] ? match : MODULE.TYPE([param], [exp[0]], 1); 1284 | var monad = inbound.__LazyChain__.monad; 1285 | if (!match) { 1286 | continue; 1287 | } 1288 | else if (etype === 1) { 1289 | r[r.length] = param; 1290 | return r; 1291 | } 1292 | else if (etype === 2) { 1293 | r[r.length] = monad.bind(param, exp[1]); 1294 | return r; 1295 | } 1296 | else if (etype === 3 && monad.bind(param, exp[1])) { 1297 | r[r.length] = monad.bind(param, exp[2]); 1298 | return r; 1299 | } 1300 | } 1301 | r[r.length] = monad.fail(param); 1302 | return r; 1303 | }, []); 1304 | } 1305 | } 1306 | MODEL.monadic = monadic; 1307 | function emulator(inbound, options, method, provider, offset) { 1308 | var outbound = inbound.__LazyChain__.child = cascade(inbound, null); 1309 | inbound 1310 | .progress(function () { 1311 | var params = []; 1312 | for (var _i = 0; _i < arguments.length; _i++) { 1313 | params[_i - 0] = arguments[_i]; 1314 | } 1315 | return MODULE.APPLY(outbound.notify, void 0, conv(params)); 1316 | }) 1317 | .done(function () { 1318 | var params = []; 1319 | for (var _i = 0; _i < arguments.length; _i++) { 1320 | params[_i - 0] = arguments[_i]; 1321 | } 1322 | return MODULE.APPLY(outbound.resolve, void 0, conv(params)); 1323 | }) 1324 | .fail(function () { 1325 | var params = []; 1326 | for (var _i = 0; _i < arguments.length; _i++) { 1327 | params[_i - 0] = arguments[_i]; 1328 | } 1329 | return MODULE.APPLY(outbound.reject, void 0, conv(params)); 1330 | }); 1331 | return outbound; 1332 | function conv(params) { 1333 | var result = offset ? MODULE.APPLY(provider[method], provider, [params].concat(options)) 1334 | : MODULE.APPLY(provider[method], params, options); 1335 | var messages; 1336 | switch (method) { 1337 | case 'every': 1338 | case 'some': 1339 | messages = result ? params : []; 1340 | break; 1341 | case 'reduce': 1342 | case 'reduceRight': 1343 | messages = [result]; 1344 | break; 1345 | case 'forEach': 1346 | messages = []; 1347 | break; 1348 | default: 1349 | messages = result instanceof Array ? result : []; 1350 | } 1351 | return messages; 1352 | } 1353 | } 1354 | MODEL.emulator = emulator; 1355 | })(MODEL = MODULE.MODEL || (MODULE.MODEL = {})); 1356 | })(MODULE || (MODULE = {})); 1357 | /// 1358 | var LIBRALY; 1359 | (function (LIBRALY) { 1360 | var FUNCTION; 1361 | (function (FUNCTION) { 1362 | var APPLY = LIBRALY.FUNCTION.apply; 1363 | FUNCTION.cache = function (func, size) { 1364 | if (size === void 0) { size = 5; } 1365 | var cache, margin, noarg; 1366 | var map = typeof Map === 'function' ? new Map() : null, res = []; 1367 | return function () { 1368 | var args = []; 1369 | for (var _i = 0; _i < arguments.length; _i++) { 1370 | args[_i - 0] = arguments[_i]; 1371 | } 1372 | var len = arguments.length; 1373 | if (len === 0) { 1374 | if (noarg === void 0) { 1375 | noarg = func(); 1376 | } 1377 | return noarg; 1378 | } 1379 | else if (map) { 1380 | var m = map; 1381 | for (var i = 0; i < len; i++) { 1382 | var id = args[i]; 1383 | var n = m.get(id); 1384 | if (n === void 0) { 1385 | n = new Map(); 1386 | m.set(id, n); 1387 | } 1388 | m = n; 1389 | } 1390 | var result = m.get(res); 1391 | if (result === void 0) { 1392 | result = APPLY(func, void 0, args); 1393 | if (result !== void 0) { 1394 | m.set(res, result); 1395 | } 1396 | } 1397 | return result; 1398 | } 1399 | else { 1400 | margin = margin === void 0 ? size > 20 && 10 || Math.floor(size / 2) : margin; 1401 | var node = cache = cache || Object.create(null), result; 1402 | for (var i = 0; i < len; i++) { 1403 | node = i ? result[2] : node; 1404 | var id = args[i]; 1405 | // try-catch文が存在するとIEで大幅な速度低下が発生するので隔離 1406 | var index = id && typeof id === 'object' ? stringify(id) : id + ''; 1407 | // argumentsオブジェクトをパラメータにする関数が存在するだけで 1408 | // 実行されなくともFirefoxとIEで大幅な速度低下が発生するので要注意 1409 | result = update(sort(node, index, id), id, func, args, size, margin); 1410 | } 1411 | return result[1]; 1412 | } 1413 | }; 1414 | }; 1415 | function stringify(id) { 1416 | try { 1417 | return id && typeof id === 'object' ? JSON.stringify(id) : id + ''; 1418 | } 1419 | catch (e) { 1420 | return id + ''; 1421 | } 1422 | } 1423 | function sort(node, index, id) { 1424 | var results; 1425 | if (!(index in node)) { 1426 | results = node[index] = [[id, void 0, Object.create(null)]]; 1427 | } 1428 | else { 1429 | results = node[index]; 1430 | if (results[0][0] !== id) { 1431 | for (var i = 1, len = results.length; i < len; i++) { 1432 | if (results[i][0] === id) { 1433 | results.unshift(results.splice(i, 1).pop()); 1434 | break; 1435 | } 1436 | } 1437 | } 1438 | } 1439 | return results; 1440 | } 1441 | function update(results, id, func, args, size, margin) { 1442 | var result = results[0]; 1443 | if (results.length === 0 || result[0] !== id) { 1444 | result = [id, APPLY(func, void 0, args), Object.create(null)]; 1445 | results.unshift(result); 1446 | } 1447 | else if (result[1] === void 0) { 1448 | result[1] = APPLY(func, void 0, args); 1449 | } 1450 | if (results.length > size + margin) { 1451 | results.splice(size, size + margin); 1452 | } 1453 | return result; 1454 | } 1455 | })(FUNCTION = LIBRALY.FUNCTION || (LIBRALY.FUNCTION = {})); 1456 | })(LIBRALY || (LIBRALY = {})); 1457 | /// 1458 | var LIBRALY; 1459 | (function (LIBRALY) { 1460 | var UTILITY; 1461 | (function (UTILITY) { 1462 | var cache = LIBRALY.FUNCTION.cache; 1463 | UTILITY.store = function (size) { 1464 | if (size === void 0) { size = 10; } 1465 | var k2v = cache(function (key) { return value; }, size), value, store = function (key, val) { 1466 | value = val; 1467 | return k2v(key); 1468 | }; 1469 | return function (key, value) { 1470 | if (value === void 0) { 1471 | return store(key); 1472 | } 1473 | else { 1474 | if (value === store(key, value)) { 1475 | return value; 1476 | } 1477 | else { 1478 | throw Error(key); 1479 | } 1480 | } 1481 | }; 1482 | }; 1483 | })(UTILITY = LIBRALY.UTILITY || (LIBRALY.UTILITY = {})); 1484 | })(LIBRALY || (LIBRALY = {})); 1485 | /// 1486 | /// 1487 | var LIBRALY; 1488 | (function (LIBRALY) { 1489 | var FUNCTION; 1490 | (function (FUNCTION) { 1491 | var TYPE = LIBRALY.TYPE; 1492 | var APPLY = LIBRALY.FUNCTION.apply; 1493 | function dispatcher() { 1494 | var patterns = []; 1495 | for (var _i = 0; _i < arguments.length; _i++) { 1496 | patterns[_i - 0] = arguments[_i]; 1497 | } 1498 | return function () { 1499 | var params = []; 1500 | for (var _i = 0; _i < arguments.length; _i++) { 1501 | params[_i - 0] = arguments[_i]; 1502 | } 1503 | return dispatch(patterns, params); 1504 | }; 1505 | } 1506 | FUNCTION.dispatcher = dispatcher; 1507 | function dispatch(patterns, params) { 1508 | for (var i = 0; i < patterns.length; i++) { 1509 | var pattern = patterns[i], types = pattern[0], guard = pattern.length >= 2 ? pattern[2] : null, fn = pattern.length === 1 ? null 1510 | : !guard ? pattern[1] 1511 | : pattern[2]; 1512 | if (!TYPE.type(params, types)) { 1513 | continue; 1514 | } 1515 | if (!fn) { 1516 | continue; 1517 | } 1518 | if (guard && !APPLY(guard, void 0, params)) { 1519 | continue; 1520 | } 1521 | switch (params.length) { 1522 | case 0: 1523 | return fn(); 1524 | case 1: 1525 | return fn(params[0]); 1526 | case 2: 1527 | return fn(params[0], params[1]); 1528 | case 3: 1529 | return fn(params[0], params[1], params[2]); 1530 | case 4: 1531 | return fn(params[0], params[1], params[2], params[3]); 1532 | } 1533 | return APPLY(fn, void 0, params); 1534 | } 1535 | throw new Error('non-exhaustive dispatch'); 1536 | } 1537 | })(FUNCTION = LIBRALY.FUNCTION || (LIBRALY.FUNCTION = {})); 1538 | })(LIBRALY || (LIBRALY = {})); 1539 | /// 1540 | /// 1541 | /// 1542 | /// 1543 | /// 1544 | /// 1545 | /* CONTROLLER */ 1546 | var MODULE; 1547 | (function (MODULE) { 1548 | var CONTROLLER; 1549 | (function (CONTROLLER) { 1550 | function handle() { 1551 | var lazychain; 1552 | lazychain = (function () { 1553 | var args = []; 1554 | for (var _i = 0; _i < arguments.length; _i++) { 1555 | args[_i - 0] = arguments[_i]; 1556 | } 1557 | return MODULE.APPLY(CONTROLLER.core, void 0, args); 1558 | }); 1559 | lazychain.id = MODULE.ID; 1560 | lazychain.uuid = MODULE.UUID; 1561 | lazychain.args2array = LIBRALY.FUNCTION.args2array; 1562 | lazychain.type = MODULE.TYPE; 1563 | lazychain.memoize = LIBRALY.FUNCTION.memoize; 1564 | lazychain.cache = LIBRALY.FUNCTION.cache; 1565 | lazychain.store = LIBRALY.UTILITY.store; 1566 | lazychain.resolve = MODULE.PROMISE.resolve; 1567 | lazychain.reject = MODULE.PROMISE.reject; 1568 | lazychain.deferred = MODULE.PROMISE.deferred; 1569 | lazychain.when = MODULE.PROMISE.when; 1570 | lazychain.dispatcher = LIBRALY.FUNCTION.dispatcher; 1571 | lazychain.repeat = LIBRALY.UTILITY.repeat; 1572 | lazychain.duff = LIBRALY.UTILITY.duff; 1573 | lazychain.duffbk = LIBRALY.UTILITY.duffbk; 1574 | return lazychain; 1575 | } 1576 | CONTROLLER.handle = handle; 1577 | function core() { 1578 | var args = []; 1579 | for (var _i = 0; _i < arguments.length; _i++) { 1580 | args[_i - 0] = arguments[_i]; 1581 | } 1582 | switch (true) { 1583 | case MODULE.TYPE(args, [true]): 1584 | return void extendArray_(true, false); 1585 | case MODULE.TYPE(args, [false]): 1586 | return void extendArray_(false, false); 1587 | case MODULE.TYPE(args, [true, Array.prototype]): 1588 | return void extendArray_(true, true); 1589 | case MODULE.TYPE(args, [false, Array.prototype]): 1590 | return void extendArray_(false, true); 1591 | case MODULE.TYPE(args, [true, Function]): 1592 | return void extendStream_(true, args[1], 1); 1593 | case MODULE.TYPE(args, [false, Function]): 1594 | return void extendStream_(false, args[1], 1); 1595 | case MODULE.TYPE(args, []): 1596 | return new CONTROLLER.Stream(); 1597 | case MODULE.TYPE(args, [Function]): 1598 | return new CONTROLLER.Stream(void 0, [args[0]]); 1599 | case MODULE.TYPE(args, [Array]): 1600 | return new CONTROLLER.Stream(args[0]); 1601 | case MODULE.TYPE(args, [Array, Function]): 1602 | return new CONTROLLER.Stream(args[0], [args[1]]); 1603 | case MODULE.TYPE(args, [Object, Function]): 1604 | return new CONTROLLER.Stream(args[0], [args[1]]); 1605 | case MODULE.TYPE(args, [Object, MODULE.ID.rest()]): 1606 | return new CONTROLLER.Stream().stream(args); 1607 | } 1608 | } 1609 | CONTROLLER.core = core; 1610 | function extendArray_(state, enumerable) { 1611 | [ 1612 | 'lazy', 1613 | 'stream' 1614 | ] 1615 | .forEach(function (v) { 1616 | if (state) { 1617 | Object.defineProperty(Array.prototype, v, { 1618 | configurable: true, 1619 | writable: true, 1620 | enumerable: enumerable, 1621 | value: function () { 1622 | var args = []; 1623 | for (var _i = 0; _i < arguments.length; _i++) { 1624 | args[_i - 0] = arguments[_i]; 1625 | } 1626 | return MODULE.APPLY(CONTROLLER[v], void 0, [LazyChain(this)].concat(args)); 1627 | } 1628 | }); 1629 | } 1630 | else { 1631 | delete Array.prototype[v]; 1632 | } 1633 | }); 1634 | } 1635 | CONTROLLER.extendArray_ = extendArray_; 1636 | function extendStream_(state, extension, offset, method) { 1637 | if (method === void 0) { method = []; } 1638 | if (state) { 1639 | Object.keys(extension) 1640 | .concat(method) 1641 | .filter(function (v) { return !CONTROLLER.Stream.prototype[v] && 'function' === typeof extension[v]; }) 1642 | .forEach(function (v) { 1643 | CONTROLLER.Stream.prototype[v] = function () { 1644 | var args = []; 1645 | for (var _i = 0; _i < arguments.length; _i++) { 1646 | args[_i - 0] = arguments[_i]; 1647 | } 1648 | this.__LazyChain__.command = [v, args]; 1649 | return MODULE.MODEL.emulator(this, args, v, extension, offset); 1650 | }; 1651 | Object.defineProperty(CONTROLLER.Stream.prototype[v], 'provider', { 1652 | configurable: false, 1653 | writable: false, 1654 | enumerable: false, 1655 | value: extension 1656 | }); 1657 | }); 1658 | } 1659 | else { 1660 | Object.keys(extension) 1661 | .concat(method) 1662 | .filter(function (v) { return !!CONTROLLER.Stream.prototype[v] && CONTROLLER.Stream.prototype[v].provider === extension; }) 1663 | .forEach(function (v) { return delete CONTROLLER.Stream.prototype[v]; }); 1664 | } 1665 | } 1666 | CONTROLLER.extendStream_ = extendStream_; 1667 | function lazy() { 1668 | var args = []; 1669 | for (var _i = 0; _i < arguments.length; _i++) { 1670 | args[_i - 0] = arguments[_i]; 1671 | } 1672 | var context = args[0]; 1673 | switch (true) { 1674 | // basic 1675 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function]): 1676 | return MODULE.MODEL.lazy(context, args[1]); 1677 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, Boolean]): 1678 | return MODULE.MODEL.lazy(context, args[1], args[2]); 1679 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, void 0]): 1680 | return MODULE.MODEL.lazy(context, args[1], args[2]); 1681 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, void 0, Boolean]): 1682 | return MODULE.MODEL.lazy(context, args[1], args[2], args[3]); 1683 | // immediate 1684 | case MODULE.TYPE(args, [CONTROLLER.Stream]): 1685 | return MODULE.MODEL.immediate(context); 1686 | // buffer 1687 | case MODULE.TYPE(args, [CONTROLLER.Stream, Number]): 1688 | return MODULE.MODEL.buffer(context, args[1]); 1689 | default: 1690 | return MODULE.MODEL.stream(context, void 0); 1691 | } 1692 | } 1693 | CONTROLLER.lazy = lazy; 1694 | function stream() { 1695 | var args = []; 1696 | for (var _i = 0; _i < arguments.length; _i++) { 1697 | args[_i - 0] = arguments[_i]; 1698 | } 1699 | var context = args[0]; 1700 | switch (true) { 1701 | // basic 1702 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function]): 1703 | return MODULE.MODEL.stream(context, args[1]); 1704 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, void 0]): 1705 | return MODULE.MODEL.stream(context, args[1], args[2]); 1706 | // compose 1707 | case MODULE.TYPE(args, [CONTROLLER.Stream, Object, MODULE.ID.rest()]): 1708 | return MODULE.MODEL.compose(context, args.slice(1)); 1709 | // branch 1710 | case MODULE.TYPE(args, [CONTROLLER.Stream]): 1711 | return context; 1712 | case MODULE.TYPE(args, [CONTROLLER.Stream, true, [Object]]): 1713 | return MODULE.MODEL.branch(context, args[2]); 1714 | case MODULE.TYPE(args, [CONTROLLER.Stream, false, [Object]]): 1715 | return MODULE.MODEL.branch(context, args[2]), MODULE.MODEL.cascade(context, null); 1716 | // split 1717 | case MODULE.TYPE(args, [CONTROLLER.Stream, Array, Object]): 1718 | case MODULE.TYPE(args, [CONTROLLER.Stream, Array, null]): 1719 | case MODULE.TYPE(args, [CONTROLLER.Stream, Array, Object]): 1720 | case MODULE.TYPE(args, [CONTROLLER.Stream, Array, null]): 1721 | return MODULE.MODEL.split(context, function (v) { return MODULE.TYPE([v], args[1]); }, args[2]); 1722 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, Object, void 0]): 1723 | case MODULE.TYPE(args, [CONTROLLER.Stream, Function, [Object], void 0]): 1724 | return MODULE.MODEL.split(context, args[1], args[2], args[3]); 1725 | case MODULE.TYPE(args, [CONTROLLER.Stream, String, Object]): 1726 | case MODULE.TYPE(args, [CONTROLLER.Stream, String, null]): 1727 | return MODULE.MODEL.split(context, function (v) { return MODULE.TYPE(v) === args[1]; }, args[2]); 1728 | // merge 1729 | case MODULE.TYPE(args, [CONTROLLER.Stream, [Object]]): 1730 | return MODULE.MODEL.merge(context, args[1]); 1731 | default: 1732 | return MODULE.MODEL.stream(context, void 0); 1733 | } 1734 | } 1735 | CONTROLLER.stream = stream; 1736 | function pattern(context) { 1737 | var patterns = []; 1738 | for (var _i = 1; _i < arguments.length; _i++) { 1739 | patterns[_i - 1] = arguments[_i]; 1740 | } 1741 | return MODULE.MODEL.pattern(context, patterns); 1742 | } 1743 | CONTROLLER.pattern = pattern; 1744 | function monad(context, monad, convert) { 1745 | return MODULE.MODEL.monad(context, monad, convert); 1746 | } 1747 | CONTROLLER.monad = monad; 1748 | function monadic(context) { 1749 | var patterns = []; 1750 | for (var _i = 1; _i < arguments.length; _i++) { 1751 | patterns[_i - 1] = arguments[_i]; 1752 | } 1753 | return MODULE.MODEL.monadic(context, patterns); 1754 | } 1755 | CONTROLLER.monadic = monadic; 1756 | function mixin(context, source, method, offset) { 1757 | if (method === void 0) { method = []; } 1758 | if (offset === void 0) { offset = 1; } 1759 | // deferred 1760 | Object.keys(source) 1761 | .concat(method) 1762 | .filter(function (method) { return !context[method] && 'function' === typeof source[method]; }) 1763 | .forEach(function (method) { return context[method] = source[method]; }); 1764 | return context; 1765 | } 1766 | CONTROLLER.mixin = mixin; 1767 | function emulate(context, source, method, offset, overwrite) { 1768 | if (method === void 0) { method = []; } 1769 | if (offset === void 0) { offset = 1; } 1770 | if (overwrite === void 0) { overwrite = false; } 1771 | // array, extension 1772 | Object.keys(source) 1773 | .concat(method) 1774 | .filter(function (method) { return overwrite || !context[method] && 'function' === typeof source[method]; }) 1775 | .forEach(function (method) { return context[method] = function () { 1776 | var args = []; 1777 | for (var _i = 0; _i < arguments.length; _i++) { 1778 | args[_i - 0] = arguments[_i]; 1779 | } 1780 | context.__LazyChain__.command = [method, args]; 1781 | return MODULE.MODEL.emulator(context, args, method, source, offset); 1782 | }; }); 1783 | return context; 1784 | } 1785 | CONTROLLER.emulate = emulate; 1786 | })(CONTROLLER = MODULE.CONTROLLER || (MODULE.CONTROLLER = {})); 1787 | })(MODULE || (MODULE = {})); 1788 | /// 1789 | var LazyChain; 1790 | var MODULE; 1791 | (function (MODULE) { 1792 | LazyChain = LazyChain || MODULE.CONTROLLER.handle(); 1793 | switch (true) { 1794 | case typeof module === 'object' && !!module && module.exports instanceof Object: 1795 | module.exports = LazyChain; 1796 | break; 1797 | case typeof window === 'object' && !!window && window.window === window: 1798 | window.LazyChain = LazyChain; 1799 | break; 1800 | } 1801 | })(MODULE || (MODULE = {})); 1802 | }("lazychain", "0.3.0-alpha.23"); 1803 | 1804 | },{}],13:[function(require,module,exports){ 1805 | "use strict"; 1806 | var KEYS_DOWN = new Set(); 1807 | var HOTKEY_CODES = {}; 1808 | 1809 | function getSettings(callback) { 1810 | chrome.storage.local.get(["extension-settings"], function (result) { 1811 | let settingsFull = result["extension-settings"]; 1812 | HOTKEY_CODES = settingsFull.hotkeys.codes; 1813 | 1814 | if (callback instanceof Function) { 1815 | callback(); 1816 | } 1817 | }); 1818 | } 1819 | exports.getSettings = getSettings; 1820 | 1821 | function isHotkeyPressed(hotkeyID) { 1822 | return HOTKEY_CODES[hotkeyID].some((combo) => { 1823 | return setArrayMatch(KEYS_DOWN, combo); 1824 | }) 1825 | } 1826 | exports.isHotkeyPressed = isHotkeyPressed; 1827 | 1828 | // Functions for handling keys down 1829 | // ------------------------------------------ 1830 | function startListeners() { 1831 | window.addEventListener("keyup", removeFromKeysDown); 1832 | 1833 | //* BUG-FIX: Changing tabs or windows using keyboard failed to clear the keys from the KEYS_DOWN set 1834 | window.addEventListener("blur", () => { 1835 | KEYS_DOWN.clear(); 1836 | }) 1837 | } 1838 | exports.startListeners = startListeners; 1839 | 1840 | function removeFromKeysDown(e) { 1841 | if (e.key) KEYS_DOWN.delete(e.key.toLowerCase()); 1842 | } 1843 | 1844 | function addToKeysDown(e) { 1845 | if (e.metaKey) KEYS_DOWN.add("meta"); 1846 | if (e.ctrlKey) KEYS_DOWN.add("control"); 1847 | if (e.altKey) KEYS_DOWN.add("alt"); 1848 | if (e.shiftKey) KEYS_DOWN.add("shift"); 1849 | if (e.key) KEYS_DOWN.add(e.key.toLowerCase()); 1850 | } 1851 | exports.addToKeysDown = addToKeysDown; 1852 | 1853 | 1854 | // checking equality of set values to array values, not case sensitive 1855 | function setArrayMatch(set1, array1) { 1856 | // Check if the map and array have the same number of entries 1857 | if (set1.size !== array1.length) return false; 1858 | // Check if all items exist and are in the same order 1859 | let i = 0; 1860 | for (let element of set1) { 1861 | if (element.toLowerCase() !== array1[i].toLowerCase()) return false; 1862 | i++; 1863 | } 1864 | // Otherwise, return true 1865 | return true; 1866 | } 1867 | 1868 | },{}],2:[function(require,module,exports){ 1869 | "use strict"; 1870 | var HELPER_FUNCTIONS = require('helperFunctions'); 1871 | 1872 | exports.CURSOR_ID = 'spatialnavigation-cursor'; 1873 | exports.URLDISPLAY_ID = 'spatialnavigation-urldisplay'; 1874 | exports.MARKER_TAG = 'spatialnavigation-marker'; 1875 | function attribute(event, cursor) { 1876 | return { 1877 | command: key2command(event), 1878 | cursor: cursor 1879 | }; 1880 | } 1881 | exports.attribute = attribute; 1882 | 1883 | function key2command(event) { 1884 | HELPER_FUNCTIONS.addToKeysDown(event); 1885 | // =============================================================================================================================== 1886 | // Adding a hotkey for extension disable (Nomadic 07.03.2021 ) 1887 | // =============================================================================================================================== 1888 | // UnComment the line below to have enable/disable be global to the window. Leave it commented to have the setting be tab specific 1889 | // let localStorage = window.localStorage; 1890 | 1891 | // check if the keys should be enabled or disabled 1892 | let isSpatialNavEnabled = localStorage.getItem("isEnabled"); 1893 | // make value boolean, localStorage only stores strings 1894 | isSpatialNavEnabled = isSpatialNavEnabled === "true" ? true : isSpatialNavEnabled === "false" ? false : isSpatialNavEnabled; 1895 | 1896 | // initialize stored state if not set 1897 | if (isSpatialNavEnabled === null) { 1898 | isSpatialNavEnabled = true; 1899 | localStorage.setItem("isEnabled", true); 1900 | } 1901 | 1902 | // check to see if the key combo is the enable/disable command 1903 | if (HELPER_FUNCTIONS.isHotkeyPressed("disableKeys")) { 1904 | // flip the value in storage 1905 | isSpatialNavEnabled = !isSpatialNavEnabled; 1906 | localStorage.setItem("isEnabled", isSpatialNavEnabled); 1907 | } 1908 | 1909 | // prevent other keys when disabled 1910 | if (!isSpatialNavEnabled) return 11; // INVALID 1911 | 1912 | // check to see if key combo matches an action 1913 | if(HELPER_FUNCTIONS.isHotkeyPressed("navUp")) { 1914 | return 0; 1915 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("navLeft")) { 1916 | return 4; 1917 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("navDown")) { 1918 | return 2; 1919 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("navRight")) { 1920 | return 5; 1921 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("expand")) { 1922 | return 6; 1923 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("contract")) { 1924 | return 7; 1925 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("quit")) { 1926 | return 10; 1927 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("click")) { 1928 | return 8; 1929 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("shiftClick")) { 1930 | return 9; 1931 | } else if(HELPER_FUNCTIONS.isHotkeyPressed("controlClick")) { 1932 | return 12; 1933 | } else { 1934 | return 11; // INVALID 1935 | } 1936 | } 1937 | exports.key2command = key2command; 1938 | 1939 | },{"helperFunctions":13}],3:[function(require,module,exports){ 1940 | "use strict"; 1941 | var MODEL = require('../model/model'); 1942 | var VIEW = require('../view/view'); 1943 | function Controller(targets) { 1944 | return targets.map(function (target) { return new VIEW.View(target); }); 1945 | } 1946 | exports.Controller = Controller; 1947 | function command(entity, attribute) { 1948 | MODEL.input(entity, attribute); 1949 | } 1950 | exports.command = command; 1951 | 1952 | },{"../model/model":8,"../view/view":12}],4:[function(require,module,exports){ 1953 | 1954 | },{}],5:[function(require,module,exports){ 1955 | "use strict"; 1956 | var Entity = (function () { 1957 | function Entity(viewId) { 1958 | this.viewId_ = viewId; 1959 | } 1960 | Object.defineProperty(Entity.prototype, "viewId", { 1961 | get: function () { 1962 | return this.viewId_; 1963 | }, 1964 | set: function (_) { 1965 | return; 1966 | }, 1967 | enumerable: true, 1968 | configurable: true 1969 | }); 1970 | return Entity; 1971 | })(); 1972 | exports.Entity = Entity; 1973 | 1974 | },{}],6:[function(require,module,exports){ 1975 | "use strict"; 1976 | var model = require('./model/model'); 1977 | model.main(); 1978 | 1979 | },{"./model/model":8}],7:[function(require,module,exports){ 1980 | "use strict"; 1981 | var ATTRIBUTE = require('../attribute/attribute'); 1982 | var SELECTOR = [ 1983 | 'a', 1984 | 'input', 1985 | 'select', 1986 | 'option', 1987 | 'datalist', 1988 | 'textarea', 1989 | 'button', 1990 | 'audio', 1991 | 'video', 1992 | 'embed', 1993 | '[onclick]', 1994 | '[tabindex]:not([role="tablist"]):not(ul):not(#hdtb):not(#hdtbMenus)', 1995 | //'[role="link"]', 1996 | '[role="button"]', 1997 | '[role="checkbox"]', 1998 | '[role="option"]', 1999 | '[role="tab"]', 2000 | '[role="menuitem"]' 2001 | ] 2002 | .join(','); 2003 | var queried, linkcount = 0; 2004 | function analyze(data) { 2005 | if (!queried || !data.attribute.cursor || linkcount !== document.links.length) { 2006 | queried = Array.apply(null, document.querySelectorAll(SELECTOR)); 2007 | linkcount = document.links.length; 2008 | } 2009 | var winWidth = window.innerWidth, winHeight = window.innerHeight, winTop = window.scrollY, winLeft = window.scrollX; 2010 | var targets = findTargets(queried.filter(function (target) { return isVisible(shiftVisibleImg(target)); }), data.attribute.command, data.attribute.cursor); 2011 | queried = isInWindow(targets[0]) ? queried : null; 2012 | return { 2013 | entity: data.entity, 2014 | attribute: data.attribute, 2015 | result: { 2016 | targets: targets 2017 | } 2018 | }; 2019 | function findTargets(targets, command, cursor) { 2020 | cursor = isCursorActive(cursor) ? cursor : null; 2021 | switch (command) { 2022 | case 0 /* UP */: 2023 | return !cursor 2024 | ? findLeftTops(targets) 2025 | : findCursorTops(targets, cursor); 2026 | case 1 /* UP_S */: 2027 | return !cursor 2028 | ? findLeftTops(targets) 2029 | : findCursorColumn(findCursorTops(targets, cursor), cursor); 2030 | case 2 /* DOWN */: 2031 | return !cursor 2032 | ? findMainColumn(targets) 2033 | : findCursorBottoms(targets, cursor); 2034 | case 3 /* DOWN_S */: 2035 | return !cursor 2036 | ? findMainColumn(targets) 2037 | : findCursorColumn(findCursorBottoms(targets, cursor), cursor); 2038 | case 4 /* LEFT */: 2039 | return !cursor 2040 | ? findLeftColumn(targets) 2041 | : findCursorLefts(targets, cursor); 2042 | case 5 /* RIGHT */: 2043 | return !cursor 2044 | ? findRightColumn(targets) 2045 | : findCursorRights(targets, cursor); 2046 | case 6 /* EXPAND */: 2047 | return findCursorNeerTargets(targets, cursor || findMainColumn(targets)[0] || document.body) 2048 | .filter(isInWindow); 2049 | case 7 /* CONTRACT */: 2050 | return findCursorNeerTargets(targets, cursor || findMainColumn(targets)[0] || document.body) 2051 | .filter(isInWindow) 2052 | .reverse(); 2053 | default: 2054 | return []; 2055 | } 2056 | function findLeftTops(targets) { 2057 | return targets 2058 | .filter(isInWindow) 2059 | .map(shiftVisibleImg) 2060 | .sort(compareLeftTopDistance); 2061 | } 2062 | function findMainColumn(targets) { 2063 | return columns(targets.filter(function (target) { return target.getBoundingClientRect().left < (winWidth / 2); }).filter(hasVisibleTextNode)) 2064 | .sort(compareGroupsByTextWeightAverage) 2065 | .map(function (group) { return group.filter(isInWindow); }) 2066 | .filter(function (group) { return group.length > 0; }) 2067 | .reduce(function (_, group) { return group; }, findLeftTops(targets)) 2068 | .map(shiftVisibleImg) 2069 | .sort(compareLeftTopDistance); 2070 | } 2071 | function findLeftColumn(targets) { 2072 | var mainColumn = findMainColumn(targets); 2073 | var left = mainColumn.length > 0 ? mainColumn[0].parentElement.getBoundingClientRect().left : Infinity; 2074 | return columns(targets.filter(function (target) { return target.getBoundingClientRect().right < left; })) 2075 | .map(function (group) { return group.filter(isInWindow); }) 2076 | .filter(function (group) { return group.length > 0; }) 2077 | .sort(compareGroupsByTextWeightAverage) 2078 | .reduce(function (_, group) { return group; }, mainColumn) 2079 | .map(shiftVisibleImg) 2080 | .sort(compareLeftTopDistance); 2081 | } 2082 | function findRightColumn(targets) { 2083 | var mainColumn = findMainColumn(targets); 2084 | var right = mainColumn.length > 0 ? mainColumn[0].parentElement.getBoundingClientRect().right : -Infinity; 2085 | return columns(targets.filter(function (target) { return target.getBoundingClientRect().left > right; })) 2086 | .map(function (group) { return group.filter(isInWindow); }) 2087 | .filter(function (group) { return group.length > 0; }) 2088 | .sort(compareGroupsByTextWeightAverage) 2089 | .reduce(function (_, group) { return group; }, mainColumn) 2090 | .map(shiftVisibleImg) 2091 | .sort(compareLeftTopDistance); 2092 | } 2093 | function findCursorTops(targets, cursor) { 2094 | var margin = 3; 2095 | return targets 2096 | .map(shiftVisibleImg) 2097 | .filter(isInRange(Math.max(winTop - (winHeight * 3), 0), winLeft, winLeft + winWidth, Offset(cursor).top + margin)) 2098 | .sort(compareCursorVerticalDistance(cursor)); 2099 | } 2100 | function findCursorBottoms(targets, cursor) { 2101 | var margin = 3; 2102 | return targets 2103 | .map(shiftVisibleImg) 2104 | .filter(isInRange(Offset(cursor).bottom - margin, winLeft, winLeft + winWidth, winTop + (winHeight * 4))) 2105 | .sort(compareCursorVerticalDistance(cursor)); 2106 | } 2107 | function findCursorLefts(targets, cursor) { 2108 | var margin = 3; 2109 | return targets 2110 | .map(shiftVisibleImg) 2111 | .filter(isInRange(winTop, 0, Offset(cursor).left + margin, winTop + winHeight)) 2112 | .sort(compareCursorLeftDistance(cursor)); 2113 | } 2114 | function findCursorRights(targets, cursor) { 2115 | var margin = 3; 2116 | return targets 2117 | .map(shiftVisibleImg) 2118 | .filter(isInRange(winTop, Offset(cursor).right - margin, Infinity, winTop + winHeight)) 2119 | .sort(compareCursorRightDistance(cursor)); 2120 | } 2121 | function findCursorNeerTargets(targets, cursor) { 2122 | return targets 2123 | .map(shiftVisibleImg) 2124 | .filter(isInWindow) 2125 | .sort(compareCursorDistance(cursor)); 2126 | } 2127 | function findCursorColumn(targets, cursor) { 2128 | var left = cursor.getBoundingClientRect().left; 2129 | return columns(targets.filter(isCursorColumn).filter(hasVisibleTextNode)) 2130 | .reduce(function (_, group) { return group; }, targets) 2131 | .map(shiftVisibleImg) 2132 | .sort(function (a, b) { return compareGroupsByTextWeightAverage([b], [a]) || compareLeftTopDistance(a, b); }); 2133 | function isCursorColumn(elem) { 2134 | return elem.getBoundingClientRect().left === left; 2135 | } 2136 | } 2137 | function isCursorActive(cursor) { 2138 | var rect = cursor && cursor.getBoundingClientRect(); 2139 | return !(!rect || 2140 | rect.bottom < 0 || 2141 | rect.top > winHeight || 2142 | rect.right < 0 || 2143 | rect.left > winWidth); 2144 | } 2145 | function columns(targets) { 2146 | return targets 2147 | .sort(compareLeftDistance) 2148 | .reduce(groupsByLeftDistance, []) 2149 | .map(filterFewNodesGroup) 2150 | .filter(function (group) { return group.length > 1; }); 2151 | } 2152 | function groupsByLeftDistance(groups, elem) { 2153 | if (groups.length === 0) { 2154 | return [[elem]]; 2155 | } 2156 | var group = groups.slice(-1)[0]; 2157 | return isSameGroup(group, elem) ? groups.slice(0, -1).concat([group.concat(elem)]) : groups.concat([[elem]]); 2158 | function isSameGroup(group, elem) { 2159 | return Math.floor(group[0].getBoundingClientRect().left) === Math.floor(elem.getBoundingClientRect().left); 2160 | } 2161 | } 2162 | function compareGroupsByTextWeightAverage(a, b) { 2163 | return calWeightAverage(a.filter(hasText).slice(0, 10)) - calWeightAverage(b.filter(hasText).slice(0, 10)) 2164 | || -compareLeftDistance(a[0], b[0]); 2165 | function calWeightAverage(elems) { 2166 | return calTextWeightAverage(elems); 2167 | } 2168 | function calTextWeightAverage(elems) { 2169 | return elems.length === 0 2170 | ? 0 2171 | : elems.reduce(function (r, elem) { return r + calTextWeight(elem); }, 0) / elems.length * (Math.min(elems.length, 10) / 10 + 0.5); 2172 | } 2173 | function calTextWeight(elem) { 2174 | var fontSize = parseInt(window.getComputedStyle(elem).fontSize, 10) 2175 | || parseInt(window.getComputedStyle(document.documentElement).fontSize, 10) 2176 | || 16, fullTextNodeParents = findVisibleTextNodes(elem) 2177 | .map(function (text) { return text.parentElement; }), fontWeightAverage = calFontWeightRateAverage(fullTextNodeParents), length = fullTextNodeParents 2178 | .reduce(function (r, elem) { return r + elem.textContent.trim().length; }, 0); 2179 | return fontSize * fontWeightAverage * +(length > 3); 2180 | } 2181 | function calFontWeightRateAverage(textNodeParents) { 2182 | //const sum = textNodeParents.reduce((r, elem) => r + elem.textContent.trim().length * calFontWeightRate(elem), 0), 2183 | // len = textNodeParents.reduce((r, elem) => r + elem.textContent.trim().length, 0); 2184 | //return len === 0 ? 0 : sum / len; 2185 | return textNodeParents 2186 | .sort(function (a, b) { return a.textContent.trim().length - b.textContent.trim().length; }) 2187 | .slice(-1) 2188 | .map(function (elem) { return calFontWeightRate(elem); }) 2189 | .pop(); 2190 | } 2191 | function calFontWeightRate(elem) { 2192 | var fontWeight = window.getComputedStyle(elem).fontWeight; 2193 | var weight; 2194 | switch (fontWeight) { 2195 | case 'normal': 2196 | weight = 400; 2197 | break; 2198 | case 'bold': 2199 | weight = 700; 2200 | break; 2201 | default: 2202 | weight = parseInt(fontWeight, 10); 2203 | } 2204 | return 1 + ((weight / 400 - 1) / 3); 2205 | } 2206 | } 2207 | function filterFewNodesGroup(group) { 2208 | return groupsByNodeDistanceFromRoot(group) 2209 | .filter(function (group) { return group.length > 1; }) 2210 | .reduce(function (r, group) { return r.concat(group); }, []); 2211 | } 2212 | function groupsByNodeDistanceFromRoot(group) { 2213 | return group 2214 | .sort(compareByNodeDistanceFromRoot) 2215 | .reduce(function (r, elem) { return r.length === 0 ? [[elem]] 2216 | : compareByNodeDistanceFromRoot(r[0][0], elem) === 0 ? [[elem].concat(r[0])].concat(r.slice(1)) 2217 | : [[elem]].concat(r); }, []); 2218 | } 2219 | function compareByNodeDistanceFromRoot(a, b) { 2220 | return countNodeDistanceFromRoot(a) - countNodeDistanceFromRoot(b); 2221 | function countNodeDistanceFromRoot(elem) { 2222 | var count = 0, parent = elem; 2223 | while (parent = parent.parentElement) { 2224 | ++count; 2225 | } 2226 | return count; 2227 | } 2228 | } 2229 | function compareLeftDistance(a, b) { 2230 | return Math.floor(a.getBoundingClientRect().left) - Math.floor(b.getBoundingClientRect().left); 2231 | } 2232 | function compareLeftTopDistance(a, b) { 2233 | return distance(a) - distance(b); 2234 | function distance(elem) { 2235 | var rect = elem.getBoundingClientRect(); 2236 | return Math.floor(rect.left 2237 | + rect.top * 5); 2238 | } 2239 | } 2240 | function compareCursorDistance(cursor) { 2241 | var weight = 10; 2242 | var cursorOffset = Offset(cursor); 2243 | return function (a, b) { 2244 | return distance(a) - distance(b); 2245 | }; 2246 | function distance(elem) { 2247 | var targetOffset = Offset(elem); 2248 | return Math.floor(Math.abs(targetOffset.left - cursorOffset.left) 2249 | + Math.abs(targetOffset.top - cursorOffset.top) * weight); 2250 | } 2251 | } 2252 | function compareCursorVerticalDistance(cursor) { 2253 | var weight = 3; 2254 | var cursorOffset = Offset(cursor); 2255 | return function (a, b) { 2256 | return distance(a) - distance(b); 2257 | }; 2258 | function distance(elem) { 2259 | var targetOffset = Offset(elem), hdistance = targetOffset.left <= cursorOffset.left && cursorOffset.left <= targetOffset.right 2260 | ? 0 2261 | : targetOffset.left - cursorOffset.left; 2262 | return Math.floor(Math.abs(hdistance) * weight 2263 | + Math.abs(targetOffset.top - cursorOffset.top)); 2264 | } 2265 | } 2266 | function compareCursorLeftDistance(cursor) { 2267 | var weight = 5; 2268 | var cursorOffset = Offset(cursor); 2269 | return function (a, b) { 2270 | return distance(a) - distance(b); 2271 | }; 2272 | function distance(elem) { 2273 | var targetOffset = Offset(elem); 2274 | return Math.floor(Math.abs(targetOffset.right - cursorOffset.left) 2275 | + Math.abs(targetOffset.top - cursorOffset.top) * weight); 2276 | } 2277 | } 2278 | function compareCursorRightDistance(cursor) { 2279 | var weight = 5; 2280 | var cursorOffset = Offset(cursor); 2281 | return function (a, b) { 2282 | return distance(a) - distance(b); 2283 | }; 2284 | function distance(elem) { 2285 | var targetOffset = Offset(elem); 2286 | return Math.floor(Math.abs(targetOffset.left - cursorOffset.right) 2287 | + Math.abs(targetOffset.top - cursorOffset.top) * weight); 2288 | } 2289 | } 2290 | } 2291 | function isInWindow(elem) { 2292 | return !!elem && isInRange(winTop, winLeft, winLeft + winWidth, winTop + winHeight)(elem); 2293 | } 2294 | function isInRange(top, left, right, bottom) { 2295 | return function (elem) { 2296 | var offset = Offset(elem); 2297 | return top <= offset.top && offset.top <= bottom - 10 2298 | && left <= offset.left && offset.left <= right - 10; 2299 | }; 2300 | } 2301 | function hasVisibleTextNode(elem) { 2302 | return findVisibleTextNodes(elem).length > 0; 2303 | } 2304 | function findVisibleTextNodes(elem) { 2305 | return findTextNodes(elem) 2306 | .filter(hasText) 2307 | .filter(function (text) { return isVisible(text.parentElement); }); 2308 | } 2309 | function findTextNodes(elem) { 2310 | return Array.apply(null, elem.childNodes) 2311 | .map(function (elem) { return isTextNode(elem) ? [elem] : findTextNodes(elem); }) 2312 | .reduce(function (r, elems) { return r.concat(elems); }, []); 2313 | } 2314 | function isTextNode(elem) { 2315 | return elem.nodeName === '#text'; 2316 | } 2317 | function hasText(elem) { 2318 | return elem.textContent.trim().length > 0; 2319 | } 2320 | function shiftVisibleImg(elem) { 2321 | return Array.apply(null, elem.querySelectorAll('img')) 2322 | .filter(isVisible) 2323 | .shift() || elem; 2324 | } 2325 | function isVisible(elem) { 2326 | var rect = elem.getBoundingClientRect(), point = document.elementFromPoint(Math.ceil(rect.left + (rect.width / 2)), Math.ceil(rect.top + Math.min(rect.height / 2, 10))); 2327 | return point 2328 | ? isVisibleSize(elem) && (point === elem || isChild(elem, point)) 2329 | : isVisibleSize(elem) && isVisibleStyle(elem); 2330 | function isChild(parent, child) { 2331 | return child ? child.parentElement === parent || isChild(parent, child.parentElement) : false; 2332 | } 2333 | function isVisibleSize(elem) { 2334 | return elem.offsetWidth > 9 && elem.offsetHeight > 9; 2335 | } 2336 | function isVisibleStyle(elem) { 2337 | var style = window.getComputedStyle(elem); 2338 | return (style.display.split(' ')[0] !== 'none' || 2339 | style.visibility.split(' ')[0] !== 'hidden' || 2340 | !(parseInt(style.zIndex.split(' ')[0], 10) < 0)); 2341 | } 2342 | } 2343 | function Offset(elem) { 2344 | var offset = elem.getBoundingClientRect(); 2345 | return { 2346 | top: winTop + offset.top, 2347 | left: winLeft + offset.left, 2348 | right: winLeft + offset.right, 2349 | bottom: winTop + offset.bottom, 2350 | width: offset.right - offset.left, 2351 | height: offset.bottom - offset.top 2352 | }; 2353 | } 2354 | } 2355 | exports.analyze = analyze; 2356 | 2357 | },{"../attribute/attribute":2}],8:[function(require,module,exports){ 2358 | "use strict"; 2359 | /// 2360 | var VIEW = require('../view/view'); 2361 | var CONTROLLER = require('../controller/controller'); 2362 | var STORE = require('../store/store'); 2363 | exports.store = STORE.create(); 2364 | var ANALYSIS = require('./analysis'); 2365 | //import MAP = require('./map'); 2366 | var LazyChain = require('lazychain'); 2367 | var HELPER_FUNCTIONS = require('helperFunctions'); 2368 | var views = []; 2369 | function main() { 2370 | if (document.readyState === "loading") { 2371 | return window.addEventListener("DOMContentLoaded", register); 2372 | } 2373 | else { 2374 | return register(); 2375 | } 2376 | function register() { 2377 | window.removeEventListener("DOMContentLoaded", register); 2378 | HELPER_FUNCTIONS.getSettings(); 2379 | HELPER_FUNCTIONS.startListeners(); 2380 | CONTROLLER.Controller([window]) 2381 | .forEach(function (view) { return views.unshift(view); }); 2382 | } 2383 | } 2384 | exports.main = main; 2385 | var stream = LazyChain(); 2386 | stream 2387 | .lazy(10) 2388 | .reduceRight(function (v) { return v; }) 2389 | .map(ANALYSIS.analyze) 2390 | .stream(output); 2391 | function input(entity, attribute) { 2392 | stream.notify({ entity: entity, attribute: attribute, result: null }); 2393 | } 2394 | exports.input = input; 2395 | function output(data) { 2396 | exports.store.update(data.entity.viewId, data.result); 2397 | VIEW.emit(data.entity, data.attribute); 2398 | } 2399 | 2400 | },{"../controller/controller":3,"../store/store":10,"../view/view":12,"./analysis":7,"lazychain":1,"helperFunctions":13}],9:[function(require,module,exports){ 2401 | "use strict"; 2402 | var state_ = 0 /* ENABLE */; 2403 | function state(enable) { 2404 | switch (enable) { 2405 | case true: 2406 | state_ = 0 /* ENABLE */; 2407 | break; 2408 | case false: 2409 | state_ = 1 /* DISABLE */; 2410 | break; 2411 | } 2412 | return state_ === 0 /* ENABLE */; 2413 | } 2414 | exports.state = state; 2415 | 2416 | },{}],10:[function(require,module,exports){ 2417 | "use strict"; 2418 | function create() { 2419 | return new Store(); 2420 | } 2421 | exports.create = create; 2422 | var Store = (function () { 2423 | function Store() { 2424 | this.state_ = {}; 2425 | this.diff_ = {}; 2426 | } 2427 | Store.prototype.update = function (id, diff) { 2428 | var curState = this.state_[id] = this.state_[id] || {}, curDiff = this.diff_[id] = this.diff_[id] || []; 2429 | Object.keys(diff) 2430 | .forEach(function (v) { 2431 | var key = v, val = diff[key]; 2432 | if (val === curState[key]) { 2433 | return; 2434 | } 2435 | curState[key] = val; 2436 | curDiff.push(key); 2437 | }); 2438 | }; 2439 | Store.prototype.state = function (id) { 2440 | return this.state_[id] = this.state_[id] || {}; 2441 | }; 2442 | Store.prototype.diff = function (id) { 2443 | var diff = this.diff_[id] = this.diff_[id] || [], uniq = diff.sort().reduce(function (r, v, i) { return i === 0 ? [v] : r[0] === v ? r : [v].concat(r); }, []).reverse(); 2444 | [].splice.apply(diff, [0, diff.length].concat(uniq)); 2445 | return diff; 2446 | }; 2447 | return Store; 2448 | })(); 2449 | 2450 | },{}],11:[function(require,module,exports){ 2451 | "use strict"; 2452 | var ATTRIBUTE = require('../attribute/attribute'); 2453 | function map(targets, callback, reverse, stack) { 2454 | if (stack === void 0) { stack = []; } 2455 | if (targets.length === 0) { 2456 | return []; 2457 | } 2458 | var scrollTop = window.scrollY, scrollLeft = window.scrollX; 2459 | var keys = 'abcdfghijklmnoprstuvwxyz'.split(''), container = document.createElement('div'), observer = document.createElement('input'), table = {}; 2460 | observer.style.cssText = [ 2461 | 'position: fixed;', 2462 | 'width: 0px;', 2463 | 'height: 0px;', 2464 | 'bottom: 0px;', 2465 | 'right: 0px;', 2466 | 'margin: 0px;', 2467 | 'border-width: 0px;', 2468 | 'padding: 0px;', 2469 | 'z-index: -1;' 2470 | ] 2471 | .map(function (str) { return str.split(';')[0] + ' !important;'; }) 2472 | .join(''); 2473 | observer.addEventListener('input', handler); 2474 | observer.addEventListener('blur', handler); 2475 | document.body.appendChild(observer); 2476 | observer.focus(); 2477 | setTimeout(function () { return observer.focus(); }, 1000); 2478 | var markers = targets.slice(0, keys.length) 2479 | .map(function (target, i) { 2480 | var marker = document.createElement('span'), key = keys[i], offset = calOffset(target); 2481 | marker.classList.add(ATTRIBUTE.MARKER_TAG); 2482 | marker.classList.add(ATTRIBUTE.MARKER_TAG + '-' + key); 2483 | marker.style.cssText = [ 2484 | 'position: absolute;', 2485 | 'overflow: visible;', 2486 | 'z-index: 9999;', 2487 | 'top: ' + (offset.top - 3) + 'px;', 2488 | 'left: ' + (offset.left - 12) + 'px;', 2489 | 'min-width: 5px;', 2490 | 'margin: 0px;', 2491 | 'border: 0px;', 2492 | 'padding: 1px 3px;', 2493 | 'border-radius: 3px;', 2494 | 'box-shadow: 1px 1px 1px 1px;', 2495 | 'background-color: gold;', 2496 | 'font-family: Helvetica, Arial, sans-serif;', 2497 | 'text-transform: uppercase;', 2498 | 'font-size: 13px;', 2499 | 'font-weight: bold;', 2500 | 'line-height: normal;', 2501 | 'color: black;' 2502 | ] 2503 | .map(function (str) { return str.split(';')[0] + ' !important;'; }) 2504 | .join(''); 2505 | marker.textContent = key; 2506 | table[key] = target; 2507 | container.appendChild(marker); 2508 | return target; 2509 | }); 2510 | document.body.appendChild(container); 2511 | return markers; 2512 | function handler(event) { 2513 | event.preventDefault(); 2514 | event.stopImmediatePropagation(); 2515 | // TODO: find way to detect Ctrl key 2516 | var key = ja2en(event.target.value), shiftKey = key === key.toUpperCase(), target = table[key.toLowerCase()], controlKey = false; 2517 | observer.removeEventListener('keydown', handler); 2518 | observer.removeEventListener('blur', handler); 2519 | container.remove(); 2520 | if (key && target) { 2521 | callback(target, shiftKey, controlKey); 2522 | } 2523 | observer.blur(); 2524 | setTimeout(function () { return observer.remove(); }, 1); 2525 | // TODO: Decouple from E key 2526 | switch (key) { 2527 | case !reverse ? 'e' : 'E': 2528 | if (targets.length > keys.length) { 2529 | map(targets.slice(keys.length), callback, reverse, stack.concat(targets.slice(0, keys.length))); 2530 | } 2531 | else { 2532 | map(stack.concat(targets), callback, reverse); 2533 | } 2534 | break; 2535 | case !reverse ? 'E' : 'e': 2536 | if (stack.length === 0) { 2537 | stack = targets; 2538 | targets = []; 2539 | } 2540 | if (stack.length > keys.length) { 2541 | map(stack.slice(-keys.length).concat(targets), callback, reverse, stack.slice(0, Math.max(stack.length - keys.length, 0))); 2542 | } 2543 | else { 2544 | map(stack.concat(targets), callback, reverse); 2545 | } 2546 | break; 2547 | } 2548 | } 2549 | function calOffset(elem) { 2550 | var offset = elem.getBoundingClientRect(); 2551 | return { 2552 | top: scrollTop + offset.top, 2553 | left: scrollLeft + offset.left, 2554 | right: scrollLeft + offset.right, 2555 | bottom: scrollTop + offset.bottom 2556 | }; 2557 | } 2558 | } 2559 | exports.map = map; 2560 | function ja2en(char) { 2561 | switch (char) { 2562 | case 'q': 2563 | return 'q'; 2564 | case 'Q': 2565 | return 'Q'; 2566 | case 'w': 2567 | return 'w'; 2568 | case 'W': 2569 | return 'W'; 2570 | case 'え': 2571 | return 'e'; 2572 | case 'E': 2573 | return 'E'; 2574 | case 'r': 2575 | return 'r'; 2576 | case 'R': 2577 | return 'R'; 2578 | case 't': 2579 | return 't'; 2580 | case 'T': 2581 | return 'T'; 2582 | case 'y': 2583 | return 'y'; 2584 | case 'Y': 2585 | return 'Y'; 2586 | case 'う': 2587 | return 'u'; 2588 | case 'U': 2589 | return 'U'; 2590 | case 'い': 2591 | return 'i'; 2592 | case 'I': 2593 | return 'I'; 2594 | case 'お': 2595 | return 'o'; 2596 | case 'O': 2597 | return 'O'; 2598 | case 'p': 2599 | return 'p'; 2600 | case 'P': 2601 | return 'P'; 2602 | case 'あ': 2603 | return 'a'; 2604 | case 'A': 2605 | return 'A'; 2606 | case 's': 2607 | return 's'; 2608 | case 'S': 2609 | return 'S'; 2610 | case 'd': 2611 | return 'd'; 2612 | case 'D': 2613 | return 'D'; 2614 | case 'f': 2615 | return 'f'; 2616 | case 'F': 2617 | return 'F'; 2618 | case 'g': 2619 | return 'g'; 2620 | case 'G': 2621 | return 'G'; 2622 | case 'h': 2623 | return 'h'; 2624 | case 'H': 2625 | return 'H'; 2626 | case 'j': 2627 | return 'j'; 2628 | case 'J': 2629 | return 'J'; 2630 | case 'k': 2631 | return 'k'; 2632 | case 'K': 2633 | return 'K'; 2634 | case 'l': 2635 | return 'l'; 2636 | case 'L': 2637 | return 'L'; 2638 | case 'z': 2639 | return 'z'; 2640 | case 'Z': 2641 | return 'Z'; 2642 | case 'x': 2643 | return 'x'; 2644 | case 'X': 2645 | return 'X'; 2646 | case 'c': 2647 | return 'c'; 2648 | case 'C': 2649 | return 'C'; 2650 | case 'v': 2651 | return 'v'; 2652 | case 'V': 2653 | return 'V'; 2654 | case 'b': 2655 | return 'b'; 2656 | case 'B': 2657 | return 'B'; 2658 | case 'n': 2659 | return 'n'; 2660 | case 'N': 2661 | return 'N'; 2662 | case 'm': 2663 | return 'm'; 2664 | case 'M': 2665 | return 'M'; 2666 | default: 2667 | return char; 2668 | } 2669 | } 2670 | 2671 | },{"../attribute/attribute":2}],12:[function(require,module,exports){ 2672 | "use strict"; 2673 | var ENTITY = require('../entity/entity'); 2674 | var ATTRIBUTE = require('../attribute/attribute'); 2675 | var MODEL = require('../model/model'); 2676 | var CONTROLLER = require('../controller/controller'); 2677 | var STATE = require('../state/module'); 2678 | var MAP = require('./map'); 2679 | var id = 0; 2680 | var views = {}; 2681 | exports.state = STATE.state; 2682 | var View = (function () { 2683 | function View(target) { 2684 | this.id_ = ++id; 2685 | this.style = document.createElement('style'); 2686 | views[this.id_] = this; 2687 | this.target_ = target; 2688 | this.handler_ = this.handler_.bind(this); 2689 | this.observe_(); 2690 | this.style.innerHTML = [ 2691 | '.' + ATTRIBUTE.CURSOR_ID + ' {', 2692 | ' outline: 3px solid gold !important;', 2693 | ' outline-offset: -3px !important;', 2694 | ' opacity: unset !important;', 2695 | ' background-color: rgba(255, 255, 0, 0.4) !important;', 2696 | '}', 2697 | 'img.' + ATTRIBUTE.CURSOR_ID + ' {', 2698 | ' outline-offset: -3px !important;', 2699 | '}' 2700 | ].join('\n'); 2701 | } 2702 | View.prototype.handler_ = function (event) { 2703 | undisplayUrl(); 2704 | if (!exports.state()) { 2705 | return; 2706 | } 2707 | if (event.defaultPrevented) { 2708 | return; 2709 | } 2710 | if (isInserting(event.srcElement)) { 2711 | return; 2712 | } 2713 | var cursor = document.querySelector('.' + ATTRIBUTE.CURSOR_ID), entity = new ENTITY.Entity(this.id_), attribute = ATTRIBUTE.attribute(event, cursor); 2714 | if (attribute.command === 11 /* INVALID */) { 2715 | return; 2716 | } 2717 | if (!cursor && attribute.command === 8 /* ENTER */) { 2718 | return; 2719 | } 2720 | if (!cursor && attribute.command === 9 /* ENTER_S */) { 2721 | return; 2722 | } 2723 | event.preventDefault(); 2724 | event.stopImmediatePropagation(); 2725 | CONTROLLER.command(entity, attribute); 2726 | return; 2727 | function isInserting(elem) { 2728 | switch (elem.tagName.toLowerCase()) { 2729 | case 'input': 2730 | switch (elem.getAttribute('type')) { 2731 | case 'checkbox': 2732 | case 'radio': 2733 | case 'file': 2734 | case 'submit': 2735 | case 'reset': 2736 | case 'button': 2737 | case 'image': 2738 | case 'range': 2739 | case 'color': 2740 | return false; 2741 | } 2742 | return true; 2743 | case 'select': 2744 | return false; 2745 | case 'datalist': 2746 | case 'option': 2747 | case 'textarea': 2748 | return true; 2749 | } 2750 | switch (elem.getAttribute('role')) { 2751 | case 'textbox': 2752 | return true; 2753 | } 2754 | do { 2755 | if (elem.contentEditable === 'true') { 2756 | return true; 2757 | } 2758 | } while (elem = elem.parentElement); 2759 | return false; 2760 | } 2761 | }; 2762 | View.prototype.observe_ = function () { 2763 | this.target_.addEventListener('keydown', this.handler_, true); 2764 | }; 2765 | View.prototype.release_ = function () { 2766 | this.target_.removeEventListener('keydown', this.handler_, true); 2767 | }; 2768 | View.prototype.destructor = function () { 2769 | this.release_(); 2770 | delete views[this.id_]; 2771 | }; 2772 | View.prototype.update = function (command) { 2773 | if (!this.style.parentElement) { 2774 | document.head.appendChild(this.style); 2775 | } 2776 | var state = MODEL.store.state(this.id_), diff = MODEL.store.diff(this.id_); 2777 | var key; 2778 | while (key = diff.shift()) { 2779 | switch (key) { 2780 | case 'targets': 2781 | markTarget(state.targets); 2782 | } 2783 | } 2784 | function markTarget(targets) { 2785 | switch (command) { 2786 | case 0 /* UP */: 2787 | case 1 /* UP_S */: 2788 | case 2 /* DOWN */: 2789 | case 3 /* DOWN_S */: 2790 | case 4 /* LEFT */: 2791 | case 5 /* RIGHT */: 2792 | var target = targets[0]; 2793 | if (!target) { 2794 | break; 2795 | } 2796 | select(target); 2797 | target.scrollIntoViewIfNeeded(); 2798 | break; 2799 | case 6 /* EXPAND */: 2800 | MAP.map(targets, trigger, false); 2801 | break; 2802 | case 7 /* CONTRACT */: 2803 | MAP.map(targets, trigger, true); 2804 | break; 2805 | case 8 /* ENTER */: 2806 | trigger(document.querySelector('.' + ATTRIBUTE.CURSOR_ID), false, false); 2807 | break; 2808 | case 9 /* ENTER_S */: 2809 | trigger(document.querySelector('.' + ATTRIBUTE.CURSOR_ID), true, false); 2810 | break; 2811 | case 12 /* ENTER_C */: 2812 | trigger(document.querySelector('.' + ATTRIBUTE.CURSOR_ID), false, true); 2813 | break; 2814 | default: 2815 | unselect(); 2816 | } 2817 | return; 2818 | function select(elem) { 2819 | unselect(); 2820 | displayUrl(elem); 2821 | elem.classList.add(ATTRIBUTE.CURSOR_ID); 2822 | } 2823 | function unselect() { 2824 | var selector = document.querySelector('.' + ATTRIBUTE.CURSOR_ID); 2825 | if (!selector) { 2826 | return; 2827 | } 2828 | selector.classList.remove(ATTRIBUTE.CURSOR_ID); 2829 | undisplayUrl(); 2830 | } 2831 | function trigger(cursor, shiftKey, ctrlKey) { 2832 | if (!cursor) { 2833 | return; 2834 | } 2835 | if (!document.elementFromPoint(cursor.getBoundingClientRect().left, cursor.getBoundingClientRect().top)) { 2836 | return; 2837 | } 2838 | if (cursor.tagName.toLowerCase() === 'a' 2839 | || cursor.parentElement.tagName.toLowerCase() === 'a' 2840 | || cursor.onclick 2841 | || cursor.tagName.toLowerCase() === 'option' 2842 | || -1 < ['button'].indexOf(cursor.getAttribute('role'))) { 2843 | select(cursor); 2844 | } 2845 | else { 2846 | unselect(); 2847 | } 2848 | cursor.focus(); 2849 | click(cursor, shiftKey, ctrlKey); 2850 | } 2851 | } 2852 | }; 2853 | View.prototype.destroy = function () { 2854 | this.destructor(); 2855 | }; 2856 | return View; 2857 | })(); 2858 | exports.View = View; 2859 | function emit(entity, attribute) { 2860 | var viewId = entity.viewId; 2861 | if (viewId in views) { 2862 | views[viewId].update(attribute.command); 2863 | return true; 2864 | } 2865 | else { 2866 | return false; 2867 | } 2868 | } 2869 | exports.emit = emit; 2870 | function displayUrl(cursor) { 2871 | if (!cursor) { 2872 | return; 2873 | } 2874 | if (cursor.tagName.toLowerCase() !== 'a') { 2875 | return displayUrl(cursor.parentElement); 2876 | } 2877 | var display = document.createElement('span'); 2878 | display.id = ATTRIBUTE.URLDISPLAY_ID; 2879 | display.style.cssText = [ 2880 | 'position: fixed;', 2881 | 'z-index: 9999;', 2882 | 'left: 0px;', 2883 | 'bottom: 0px;', 2884 | 'padding: 5px;', 2885 | 'color: #ececec;', 2886 | 'background-color: #26272A;', 2887 | 'border-radius: 2px;', 2888 | 'font-family: Meiryo, Helvetica, sans-serif;', 2889 | 'font-size: 11.5px;', 2890 | 'text-align: left;' 2891 | ] 2892 | .map(function (str) { return str.split(';')[0] + ' !important;'; }) 2893 | .join(''); 2894 | display.textContent = cursor.href; 2895 | document.body.appendChild(display); 2896 | } 2897 | function undisplayUrl() { 2898 | var display = document.querySelector('#' + ATTRIBUTE.URLDISPLAY_ID); 2899 | if (!display) { 2900 | return; 2901 | } 2902 | display.remove(); 2903 | } 2904 | function click(elem, shiftKey, ctrlKey) { 2905 | var target = elem.hasAttribute('target') && elem.getAttribute('target'); 2906 | if (elem.tagName.toLowerCase() === 'a') { 2907 | elem.removeAttribute('target'); 2908 | } 2909 | ["mouseover", "mousedown", "mouseup", "click"] 2910 | .forEach(function (sequence) { 2911 | var mouseEvent = document.createEvent("MouseEvents"); 2912 | mouseEvent.initMouseEvent(sequence, true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, shiftKey, false, 0, null); 2913 | elem.dispatchEvent(mouseEvent); 2914 | }); 2915 | if (elem.tagName.toLowerCase() === 'a') { 2916 | typeof target === 'boolean' ? elem.removeAttribute('target') : elem.setAttribute('target', target); 2917 | } 2918 | } 2919 | 2920 | },{"../attribute/attribute":2,"../controller/controller":3,"../entity/entity":5,"../model/model":8,"../state/module":9,"./map":11}]},{},[2,3,4,5,6,7,8,9,10,11,12,13]); 2921 | }("spatial-navigation", "0.4.7"); 2922 | --------------------------------------------------------------------------------