[READ-ONLY] Mirror of https://github.com/thoda-dev/ffxiv-miniparse. A simple skin for the ACT overlay for Final Fantasy XIV. // Un simple skin pour l'overlay d'ACT pour Final Fantasy XIV.
0

Configure Feed

Select the types of activity you want to include in your feed.

base commit

Thomas (Jul 11, 2021, 3:44 PM +0200) 09082605 af5e5471

+1194
+12
.github/FUNDING.yml
··· 1 + # These are supported funding model platforms 2 + 3 + github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 + patreon: # Replace with a single Patreon username 5 + open_collective: # Replace with a single Open Collective username 6 + ko_fi: jericho1060 7 + tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 + community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 + liberapay: # Replace with a single Liberapay username 10 + issuehunt: # Replace with a single IssueHunt username 11 + otechie: # Replace with a single Otechie username 12 + custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+5
.idea/.gitignore
··· 1 + # Default ignored files 2 + /shelf/ 3 + /workspace.xml 4 + # Editor-based HTTP Client requests 5 + /httpRequests/
+12
.idea/ffxiv-miniparse.iml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <module type="WEB_MODULE" version="4"> 3 + <component name="NewModuleRootManager"> 4 + <content url="file://$MODULE_DIR$"> 5 + <excludeFolder url="file://$MODULE_DIR$/temp" /> 6 + <excludeFolder url="file://$MODULE_DIR$/.tmp" /> 7 + <excludeFolder url="file://$MODULE_DIR$/tmp" /> 8 + </content> 9 + <orderEntry type="inheritedJdk" /> 10 + <orderEntry type="sourceFolder" forTests="false" /> 11 + </component> 12 + </module>
+8
.idea/modules.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="ProjectModuleManager"> 4 + <modules> 5 + <module fileurl="file://$PROJECT_DIR$/.idea/ffxiv-miniparse.iml" filepath="$PROJECT_DIR$/.idea/ffxiv-miniparse.iml" /> 6 + </modules> 7 + </component> 8 + </project>
+6
.idea/vcs.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <project version="4"> 3 + <component name="VcsDirectoryMappings"> 4 + <mapping directory="$PROJECT_DIR$" vcs="Git" /> 5 + </component> 6 + </project>
+23
README.MD
··· 1 + # ffxiv-miniparse 2 + 3 + A simple skin for the ACT overlay for Final Fantasy XIV 4 + 5 + ## Original Creator and credits // Créateur original et Crédits 6 + 7 + I would like to thank you the original creator but i do not know his name. From how the files are named, I would like to say it's Torgan, but i'm not sure about it. // Je voudrais remercier l'auteur original mais je ne connais malheureusement pas son nom. A la vue du nom des fichiers, je suppose que c'est Torgan, mais je ne peux pas en être certain. 8 + 9 + The original project was downloaded from a [youtube video by Mikoto](https://youtu.be/qw-q4hQ2WTY). // Le projet original a été téléchargé depuis une [vidéo Youtube de Mikoto](https://youtu.be/qw-q4hQ2WTY). 10 + 11 + I only updated the project to make it work with Stormblood and Shadowbringer with all new job icons and colors. // Je n'ai fait que rendre le projet compatible a 100% avec Stormblood et Shadowbringer en ajoutant les icones des nouveaux jobs et les couleurs associées. 12 + 13 + ## Extension supported // Extensions supportées 14 + 15 + The miniparse overlay is compatible for all extensions of the game till Shadowbringer. // L'overlay miniparse est compatible pour toutes les extensions du jeu jusqu'à Shadowbringer. 16 + 17 + ## How to Install // Comment installer 18 + 19 + Follow the instruction of Mikoto here [https://youtu.be/qw-q4hQ2WTY](https://youtu.be/qw-q4hQ2WTY). // Suivez les instructions de Mikoto ici [https://youtu.be/qw-q4hQ2WTY](https://youtu.be/qw-q4hQ2WTY). 20 + 21 + # Support or donation // Soutenir ou faire un don 22 + 23 + if you like it, [<img src="https://github.com/Jericho1060/DU-Industry-HUD/blob/main/ressources/images/ko-fi.png?raw=true" width="150">](https://ko-fi.com/jericho1060)
+259
Torgan.MiniParse.css
··· 1 + * { 2 + font-family: "Meiryo", "Segoe UI"; 3 + font-size: 12px; 4 + box-sizing: border-box; 5 + } 6 + a { 7 + margin: 0px 2px; 8 + text-decoration: none; 9 + display: inline-block; 10 + width: 20px; 11 + height: 20px; 12 + border-radius: 20px; 13 + } 14 + #wrapper:hover .viewModes { 15 + display: block; 16 + } 17 + #wrapper:hover .time { 18 + display: none; 19 + } 20 + .viewModes { 21 + display: none; 22 + list-style-type: none; 23 + padding: 0; 24 + margin: 0; 25 + position: absolute; 26 + top: 8px; 27 + right: 8px; 28 + } 29 + li { 30 + background-color: rgb(150, 150, 150); 31 + display: inline-block; 32 + width: 20px; 33 + height: 20px; 34 + margin: 0 2px; 35 + } 36 + li:hover { 37 + border: 2px solid #ccc; 38 + } 39 + .damage { 40 + background-color: #8E4A4C; 41 + } 42 + .healing { 43 + background-color: #5EA041; 44 + } 45 + .tanking { 46 + background-color: #465BC4; 47 + } 48 + html { 49 + background-image: url(handle.png); 50 + box-sizing: border-box; 51 + overflow: hidden; 52 + height: 100%; 53 + background-position: bottom right; 54 + background-repeat: no-repeat; 55 + background-color: transparent; 56 + margin: 0; 57 + } 58 + #wrapper { 59 + padding: 10px; 60 + background-color: rgba(0, 0, 0, 0.4); 61 + box-shadow: 0 0 8px 8px rgba(0, 0, 0, 0.4); 62 + /*border-radius: 4px;*/ 63 + margin: 12px; 64 + position: relative; 65 + } 66 + .dpsMeterBody * { 67 + color: #FFF7E8; 68 + text-shadow: -1px 0 3px #38352F, 0 1px 3px #38352F, 1px 0 3px #38352F, 0 -1px 3px #38352F; 69 + white-space: nowrap; 70 + text-overflow: ellipsis; 71 + } 72 + .you .name { 73 + color: #FFF7E8; 74 + text-shadow: -1px 0 3px #38352F, 0 1px 3px #38352F, 1px 0 3px #38352F, 0 -1px 3px #38352F; 75 + } 76 + .dpsMeterBody { 77 + position: relative; 78 + z-index: 1; 79 + } 80 + .dpsMeterHeader > div, 81 + .dpsMeterBody > div { 82 + height: 22px; 83 + margin: 1px 0; 84 + } 85 + .dpsMeterBody img { 86 + height: 20px; 87 + width: 20px; 88 + /* 7649 */ 89 + } 90 + .dpsMeterBody .row, 91 + .dpsMeterBody .row div { 92 + position: relative; 93 + } 94 + .dpsMeterBody .row div { 95 + position: absolute; 96 + z-index: 2; 97 + } 98 + .cell { 99 + padding: 0 5px; 100 + height: 22px; 101 + line-height: 22px; 102 + display: inline-block; 103 + vertical-align: middle; 104 + } 105 + .bar-container { 106 + position: absolute !important; 107 + height: 100%; 108 + display: inline-block; 109 + left: 22px; 110 + right: 0; 111 + /*border-radius: 0 10px 10px 0;*/ 112 + z-index: 1 !important; 113 + } 114 + .hide:hover { 115 + display: none; 116 + } 117 + .hide { 118 + display: none; 119 + } 120 + .bar { 121 + height: 100%; 122 + /* border: 1px solid rgb(25, 25, 25); 123 + border-left: 0;*/ 124 + } 125 + .gld div.bar, .pld div.bar { 126 + background: linear-gradient(to top, rgba(135, 140, 155, 0.75), rgba(200, 210, 230, 0.75)); 127 + } 128 + .pgl div.bar, .mnk div.bar { 129 + background: linear-gradient(to top, rgba(105, 80, 10, 0.75), rgba(185, 145, 15, 0.75)); 130 + } 131 + .mrd div.bar, .war div.bar { 132 + background: linear-gradient(to top, rgba(105, 15, 15, 0.75), rgba(185, 30, 25, 0.75)); 133 + } 134 + .lnc div.bar, .drg div.bar { 135 + background: linear-gradient(to top, rgba(40, 60, 125, 0.75), rgba(65, 100, 205, 0.75)); 136 + } 137 + .arc div.bar, .brd div.bar { 138 + background: linear-gradient(to top, rgba(75, 85, 35, 0.75), rgba(140, 165, 65, 0.75)); 139 + } 140 + .rog div.bar, .nin div.bar { 141 + background: linear-gradient(to top, rgba(95, 15, 55, 0.75), rgba(175, 25, 100, 0.75)); 142 + } 143 + .cnj div.bar, .whm div.bar { 144 + background: linear-gradient(to top, rgba(180, 170, 155, 0.75), rgba(255, 240, 220, 0.75)); 145 + } 146 + .thm div.bar, .blm div.bar { 147 + background: linear-gradient(to top, rgba(104, 77, 135, 0.75), rgba(165, 121, 214, 0.75)); 148 + } 149 + .acn div.bar, .smn div.bar { 150 + background: linear-gradient(to top, rgba(45, 70, 5, 0.75), rgba(90, 150, 10, 0.75)); 151 + } 152 + .sch div.bar { 153 + background: linear-gradient(to top, rgba(25, 20, 50, 0.75), rgba(70, 55, 125, 0.75)); 154 + } 155 + .drk div.bar { 156 + background: linear-gradient(to top, rgba(160, 89, 17, 0.75), rgba(239, 134, 24, 0.75)); 157 + } 158 + .mch div.bar { 159 + background: linear-gradient(to top, rgba(72, 147, 140, 0.75), rgba(110, 225, 214, 0.75)); 160 + } 161 + .ast div.bar { 162 + background: linear-gradient(to top, rgba(178, 161, 53, 0.75), rgba(255, 231, 74, 0.75)); 163 + } 164 + .sam div.bar { 165 + background: linear-gradient(to top, rgba(153, 52, 102, 0.75), rgba(153, 52, 102, 0.75)); 166 + } 167 + .rdm div.bar { 168 + background: linear-gradient(to top, rgba(166, 0, 0, 1), rgba(166, 0, 0, 1)); 169 + } 170 + .blu div.bar { 171 + background: linear-gradient(to top, rgba(10, 50, 125, 0.75), rgba(35, 90, 205, 0.75)); 172 + } 173 + .gnb div.bar { 174 + background: linear-gradient(to top, rgba(10, 50, 175, 0.75), rgba(35, 90, 255, 0.75)); 175 + } 176 + .dnc div.bar { 177 + background: linear-gradient(to top, rgba(105, 15, 15, 0.75), rgba(185, 30, 25, 0.75)); 178 + } 179 + .jobless div.bar { 180 + background: linear-gradient(to top, rgba(81, 81, 81, 0.75), rgba(160, 160, 160, 0.75)); 181 + } 182 + .dpsMeter { 183 + display: none; 184 + margin: 5px 0 0 0; 185 + } 186 + .encounter, 187 + .dpsMeterHeader * { 188 + color: #E2EBF5; 189 + text-shadow: -1px 0 3px #217AA2, 0 1px 3px #217AA2, 1px 0 3px #217AA2, 0 -1px 3px #217AA2; 190 + font-weight: bold; 191 + font-size: 14px; 192 + white-space: nowrap; 193 + } 194 + .dpsMeterHeader { 195 + border-bottom: 1px solid #DED7BE; 196 + } 197 + .encounter { 198 + padding: 0 5px; 199 + } 200 + .encounter > div { 201 + display: inline-block; 202 + width: 50%; 203 + } 204 + .encounter span { 205 + color: #FFCC66; 206 + text-shadow: -1px 0 3px #996633, 0 1px 3px #996633, 1px 0 3px #996633, 0 -1px 3px #996633; 207 + } 208 + .rdps { 209 + text-align: left; 210 + } 211 + .time { 212 + text-align: right; 213 + } 214 + .icon { 215 + width: 22px; 216 + left: 0; 217 + background-color: rgb(25, 25, 25); 218 + padding: 1px; 219 + } 220 + .name { 221 + font-size: 12px; 222 + font-weight: bold; 223 + left: 22px; 224 + } 225 + .dps { 226 + position: relative !important; 227 + text-align: right; 228 + float: right; 229 + font-weight: bold; 230 + } 231 + .dps { 232 + padding-right: 0; 233 + } 234 + .crit, 235 + .miss, 236 + .oheal, 237 + .block { 238 + text-align: right; 239 + height: 100%; 240 + float: right; 241 + } 242 + .crit, 243 + .block { 244 + position: relative !important; 245 + width: 36px; 246 + font-size: 10px; 247 + 248 + } 249 + .miss, 250 + .oheal { 251 + position: relative !important; 252 + font-size: 12px; 253 + font-weight: bold; 254 + color: #FF0000 !important; 255 + text-shadow: -1px 0 3px #270000, 0 1px 3px #270000, 1px 0 3px #270000, 0 -1px 3px #270000 !important; 256 + } 257 + .oheal { 258 + font-size: 10px; 259 + }
+82
Torgan.MiniParse.html
··· 1 + <html> 2 + <head> 3 + <meta charset="utf-8" /> 4 + <title></title> 5 + <link href="Torgan.MiniParse.css" rel="stylesheet" type="text/css" /> 6 + <style> 7 + #dpsMeterHeader { border: 0; } 8 + </style> 9 + <script type="text/javascript" src="Torgan.MiniParse.js"></script> 10 + <script> 11 + // Party members display type 12 + // FN => Full Name 13 + // S => Surname 14 + // SA => Surname Abbreviated 15 + // F => Forename 16 + // FA => Forename Abbreviated 17 + nameDisplayType = "FN"; 18 + 19 + // Your display name. Leave blank for default value. 20 + displayName = ""; 21 + 22 + maxCombatantsShown = 9; 23 + 24 + showDpsBar = true; 25 + 26 + useHTMLEncounterDefine = true; 27 + headerDefine = []; 28 + var viewModes = { 29 + damage: { 30 + encounter: "<div class=\"rdps\">Raid DPS: <span>{ENCDPS}</span></div><div class=\"time\">Time: <span>{duration}</span></div>", 31 + body: [ 32 + { html: "{icon}", class: "icon" }, 33 + { text: "{name}", class: "name" }, 34 + { text: "{crithit%}", class: "crit" }, 35 + { text: "{ENCDPS}", class: "dps" }, 36 + { text: "{misses}", class: "miss", autohide: true } 37 + ], 38 + }, 39 + healing: { 40 + encounter: "<div class=\"rdps\">Raid HPS: <span>{ENCHPS}</span></div><div class=\"time\">Time: <span>{duration}</span></div>", 41 + body: [ 42 + { html: "{icon}", class: "icon" }, 43 + { text: "{name}", class: "name" }, 44 + { text: "{critheal%}", class: "crit" }, 45 + { text: "{ENCHPS}", class: "dps" }, 46 + { text: "{OverHealPct}", class: "oheal", autohide: true } 47 + ] 48 + }, 49 + tanking: { 50 + encounter: "<div class=\"rdps\">Raid DTPS: <span>{ENCDTPS}</span></div><div class=\"time\">Time: <span>{duration}</span></div>", 51 + body: [ 52 + { html: "{icon}", class: "icon" }, 53 + { text: "{name}", class: "name" }, 54 + { text: "{ParryPct}", class: "crit" }, 55 + { text: "{ENCDTPS}", class: "dps" }, 56 + { text: "{BlockPct}", class: "block", autohide: true } 57 + ] 58 + } 59 + }; 60 + 61 + </script> 62 + </head> 63 + <body> 64 + <div id="wrapper"> 65 + <div id="encounter" class="encounter"> 66 + No data to show. 67 + <!-- ここにエンカウント情報が入る --> 68 + </div> 69 + <div id="dpsMeter" class="dpsMeter"> 70 + <div id="dpsMeterHeader" class="dpsMeterHeader"></div> 71 + <div id="dpsMeterBody" class="dpsMeterBody"></div> 72 + </div> 73 + <ul class="viewModes"><li onclick="setMode('damage');" class="damage"></li><li onclick="setMode('healing');" class="healing"></li><li onclick="setMode('tanking');" class="tanking"></li></ul> 74 + </div> 75 + <script> 76 + const encounter = document.getElementById("encounter"), 77 + dpsMeter = document.getElementById("dpsMeter"), 78 + dpsMeterHeader = document.getElementById("dpsMeterHeader"), 79 + dpsMeterBody = document.getElementById("dpsMeterBody"); 80 + </script> 81 + </body> 82 + </html>
+300
Torgan.MiniParse.js
··· 1 + var showFullName, 2 + maxCombatantsShown, 3 + showDpsBar, 4 + 5 + encounterDefine, 6 + useHTMLEncounterDefine, 7 + 8 + headerDefine, 9 + bodyDefine, 10 + viewModes, 11 + vMode = "damage", 12 + data; 13 + 14 + function setMode(mode) { 15 + vMode = mode; 16 + update(data); 17 + } 18 + 19 + // 順位を表示する(text に関数を指定する例) 20 + // 引数: 21 + // combatant : キャラクターのデータ。combatant["..."]でデータを取得できる。 22 + // index : キャラクターの並び順。一番上は 0 で、その後は 1 ずつ増える。 23 + // 戻り値: 24 + // 表示するテキスト。 25 + // ACT のタグは展開されないので、展開したい場合は parseActFormat 関数を使用してください。 26 + function rankingText(combatant, index) { 27 + // 1 から始まる番号を返す 28 + return (index + 1).toString(); 29 + } 30 + 31 + // 32 + // 以下表示用スクリプト 33 + // 34 + 35 + // onOverlayDataUpdate イベントを購読 36 + document.addEventListener("onOverlayDataUpdate", function (e) { 37 + update(e.detail); 38 + data = e.detail; 39 + }); 40 + 41 + // 表示要素の更新 42 + function update(data) { 43 + updateEncounter(data); 44 + if (dpsMeterHeader.childNodes.length === 0) { 45 + updateCombatantListHeader(); 46 + } 47 + updateCombatantList(data); 48 + } 49 + 50 + // エンカウント情報を更新する 51 + function updateEncounter(data) { 52 + // 要素取得 53 + // テキスト取得 54 + var elementText, 55 + encounterData = viewModes[vMode].encounter; 56 + if (typeof encounterData === 'function') { 57 + elementText = encounterData(data.Encounter); 58 + } else if (typeof encounterData === 'string') { 59 + elementText = parseActFormat(encounterData, data.Encounter); 60 + } else { 61 + console.log("updateEncounter: Could not update the encounter element due to invalid type."); 62 + return; 63 + } 64 + 65 + // テキスト設定 66 + if (!useHTMLEncounterDefine) { 67 + encounter.innerText = parseActFormat(encounterData, data.Encounter); 68 + } else { 69 + encounter.innerHTML = parseActFormat(encounterData, data.Encounter); 70 + } 71 + } 72 + 73 + // ヘッダを更新する 74 + function updateCombatantListHeader() { 75 + for (var i = 0; i < headerDefine.length; i++) { 76 + var cell = document.createElement("div"); 77 + dpsMeterHeader.appendChild(cell); 78 + cell.className = "cell"; 79 + // テキスト設定 80 + if (typeof headerDefine[i].text !== 'undefined') { 81 + cell.textContent = headerDefine[i].text; 82 + } else if (typeof headerDefine[i].html !== 'undefined') { 83 + cell.innerHTML = headerDefine[i].html; 84 + } 85 + if (typeof (headerDefine[i].class) !== 'undefined') { 86 + cell.className += ' ' + headerDefine[i].class; 87 + } 88 + // 幅設定 89 + cell.style.width = headerDefine[i].width; 90 + cell.style.maxWidth = headerDefine[i].width; 91 + // 横結合数設定 92 + if (typeof headerDefine[i].span !== 'undefined') { 93 + cell.colSpan = headerDefine[i].span; 94 + } 95 + // 行揃え設定 96 + if (typeof headerDefine[i].align !== 'undefined') { 97 + cell.style["textAlign"] = headerDefine[i].align; 98 + } 99 + } 100 + dpsMeter.style.display = 'block'; 101 + } 102 + 103 + 104 + // プレイヤーリストを更新する 105 + function updateCombatantList(data) { 106 + // 要素取得&作成 107 + var newBody = document.createElement("div"); 108 + newBody.id = "dpsMeterBody"; 109 + newBody.className = "dpsMeterBody"; 110 + 111 + // tbody の内容を作成 112 + var combatantIndex = 0, 113 + barRef = 0, 114 + kMode; 115 + 116 + if (vMode === "healing") kMode = "healed"; 117 + else if (vMode === "tanking") kMode = "damagetaken"; 118 + else kMode = "damage"; 119 + 120 + var CbtSorted = Object.keys(data.Combatant).sort(function (a, b) { 121 + if (+data.Combatant[a][kMode] < +data.Combatant[b][kMode]) return 1; 122 + if (+data.Combatant[a][kMode] > +data.Combatant[b][kMode]) return -1; 123 + if (a > b) return 1; 124 + if (a < b) return -1; 125 + return 0; 126 + }); 127 + 128 + if (!CbtSorted.length) return; 129 + 130 + barRef = +data.Combatant[CbtSorted[0]][kMode]; 131 + 132 + for (var c = 0; c < CbtSorted.length; c++) { 133 + var combatantName = CbtSorted[c]; 134 + var combatant = data.Combatant[combatantName], 135 + body = viewModes[vMode].body, 136 + row = document.createElement('div'); 137 + 138 + newBody.appendChild(row); 139 + row.className = 'row ' + (combatant.Job !== '' ? combatant.Job.toLowerCase() : 'jobless') + (combatantName === "YOU" ? " you" : ""); 140 + 141 + for (var i = 0; i <= body.length; i++) { 142 + var cell = document.createElement('div'); 143 + row.appendChild(cell); 144 + cell.className = 'cell'; 145 + 146 + if (i === body.length) { 147 + if (!showDpsBar) continue; 148 + var bar = document.createElement("div"); 149 + cell.appendChild(bar); 150 + cell.className = "bar-container"; 151 + var width = Math.round(combatant[kMode] * 1 / barRef * 100 * 10) / 10; 152 + if (width) { 153 + bar.style.width = width + "%"; 154 + bar.className += "bar"; 155 + } else { 156 + bar.className += "hide"; 157 + } 158 + break; 159 + } 160 + 161 + var type = typeof body[i].text !== 'undefined' ? 'text' : 'html'; 162 + 163 + // テキスト設定 164 + if (typeof body[i][type] !== 'undefined') { 165 + var content; 166 + if (typeof body[i][type] === 'function') { 167 + content = body[i][type](combatant, combatantIndex); 168 + } else { 169 + content = parseActFormat(body[i][type], combatant); 170 + } 171 + if (body[i].autohide && ((typeof content === "string" && content[0] === "0") || content === 0)) { 172 + cell.style.display = "none"; 173 + } else if (type === 'text') { 174 + cell.textContent = content; 175 + } else { 176 + cell.innerHTML = content; 177 + } 178 + } 179 + 180 + if (typeof (body[i].class) !== 'undefined') { 181 + cell.className += ' ' + body[i].class; 182 + } 183 + 184 + } 185 + 186 + if (maxCombatantsShown && ++combatantIndex === maxCombatantsShown) { 187 + break; 188 + } 189 + } 190 + 191 + // tbody が既に存在していたら置換、そうでないならテーブルに追加 192 + dpsMeter.replaceChild(newBody, dpsMeterBody); 193 + dpsMeterBody = newBody; 194 + } 195 + 196 + // Miniparse フォーマット文字列を解析し、表示文字列を取得する 197 + function parseActFormat(str, dictionary) { 198 + var result = ""; 199 + var currentIndex = 0; 200 + do { 201 + var openBraceIndex = str.indexOf('{', currentIndex); 202 + if (openBraceIndex < 0) { 203 + result += str.slice(currentIndex); 204 + break; 205 + } else { 206 + result += str.slice(currentIndex, openBraceIndex); 207 + var closeBraceIndex = str.indexOf('}', openBraceIndex); 208 + if (closeBraceIndex < 0) { 209 + // parse error! 210 + console.log("parseActFormat: Parse error: missing close-brace for " + openBraceIndex.toString() + "."); 211 + return "ERROR"; 212 + } else { 213 + var tag = str.slice(openBraceIndex + 1, closeBraceIndex); 214 + switch (tag) { 215 + case "ENCDTPS": 216 + var encdtps = +dictionary["damagetaken"] / +dictionary["DURATION"]; 217 + if (isNaN(encdtps)) encdtps = 0; 218 + else if (encdtps === Infinity) encdtps = +dictionary["damagetaken"] / +data.Encounter["DURATION"]; 219 + result += Math.round(encdtps); 220 + break; 221 + case "encdps": 222 + case "ENCDPS": 223 + if (dictionary[tag][0] === "+") { 224 + result += "---"; 225 + } else { 226 + result += dictionary[tag]; 227 + } 228 + break; 229 + case 'icon': 230 + var icon = ''; 231 + if (dictionary.Job) { 232 + icon = dictionary.Job; 233 + } else if (typeof dictionary.name === "undefined") { 234 + console.log(tag); 235 + } else if (dictionary.name.indexOf('Limit Break') === 0) { 236 + icon = 'Limit Break'; 237 + } else if (dictionary.name.indexOf('Eos (') === 0) { 238 + icon = 'Eos'; 239 + } else if (dictionary.name.indexOf('Selene (') === 0) { 240 + icon = 'Selene'; 241 + } else if (dictionary.name.indexOf('Garuda-Egi (') === 0) { 242 + icon = 'Garuda'; 243 + } else if (dictionary.name.indexOf('Ifrit-Egi (') === 0) { 244 + icon = 'Ifrit'; 245 + } else if (dictionary.name.indexOf('Titan-Egi (') === 0) { 246 + icon = 'Titan'; 247 + } else if (dictionary.name.indexOf(' (') !== -1) { 248 + icon = 'Chocobo'; 249 + } 250 + 251 + if (icon) { 252 + result += '<img src="icons/' + icon + '.png" /> '; 253 + } 254 + break; 255 + case 'name': 256 + var o; 257 + if (displayName && dictionary[tag] === "YOU") { 258 + result += displayName; 259 + } else if (nameDisplayType !== "FN" && (o = dictionary[tag].indexOf(' ')) !== -1 && dictionary[tag] !== 'Limit Break') { 260 + if (nameDisplayType == "F") { 261 + result += dictionary[tag].substr(0, o); 262 + } else if (nameDisplayType == "S") { 263 + result += dictionary[tag].substr(o); 264 + } else if (nameDisplayType == "SA") { 265 + result += dictionary[tag].substr(0, o + 2) + "."; 266 + } else if (nameDisplayType == "FA") { 267 + result += dictionary[tag].substr(0, 1) + ". " + dictionary[tag].substr(o + 1); 268 + } 269 + } else { 270 + result += dictionary[tag]; 271 + } 272 + break; 273 + case 'maxhit': 274 + if (dictionary[tag] !== '') { 275 + result += parseInt(dictionary[tag].substr(dictionary[tag].lastIndexOf('-') + 1)) || '---'; 276 + } else { 277 + result += '---'; 278 + } 279 + break; 280 + case 'misses': 281 + if (dictionary[tag] !== "0") { 282 + result += dictionary[tag]; 283 + } 284 + break; 285 + default: 286 + if (typeof dictionary[tag] !== 'undefined') { 287 + result += dictionary[tag]; 288 + } else { 289 + console.log("parseActFormat: Unknown tag: " + tag); 290 + result += "ERROR"; 291 + } 292 + 293 + } 294 + currentIndex = closeBraceIndex + 1; 295 + } 296 + } 297 + } while (currentIndex < str.length); 298 + 299 + return result; 300 + }
handle.png

This is a binary file and will not be displayed.

icons/ALC.png

This is a binary file and will not be displayed.

icons/ARM.png

This is a binary file and will not be displayed.

icons/Acn.png

This is a binary file and will not be displayed.

icons/Arc.png

This is a binary file and will not be displayed.

icons/Ast.png

This is a binary file and will not be displayed.

icons/BOT.png

This is a binary file and will not be displayed.

icons/BSM.png

This is a binary file and will not be displayed.

icons/Blm.png

This is a binary file and will not be displayed.

icons/Blu.png

This is a binary file and will not be displayed.

icons/Brd.png

This is a binary file and will not be displayed.

icons/CHO.png

This is a binary file and will not be displayed.

icons/CRP.png

This is a binary file and will not be displayed.

icons/CUL.png

This is a binary file and will not be displayed.

icons/Chocobo.png

This is a binary file and will not be displayed.

icons/Cnj.png

This is a binary file and will not be displayed.

icons/Dnc.png

This is a binary file and will not be displayed.

icons/Drg.png

This is a binary file and will not be displayed.

icons/Drk.png

This is a binary file and will not be displayed.

icons/Eos.png

This is a binary file and will not be displayed.

icons/FSH.png

This is a binary file and will not be displayed.

icons/GLA.png

This is a binary file and will not be displayed.

icons/GSM.png

This is a binary file and will not be displayed.

icons/Garuda.png

This is a binary file and will not be displayed.

icons/Gld.png

This is a binary file and will not be displayed.

icons/Gnb.png

This is a binary file and will not be displayed.

icons/Ifrit.png

This is a binary file and will not be displayed.

icons/LTW.png

This is a binary file and will not be displayed.

icons/Limit Break.png

This is a binary file and will not be displayed.

icons/Lnc.png

This is a binary file and will not be displayed.

icons/MIN.png

This is a binary file and will not be displayed.

icons/Mch.png

This is a binary file and will not be displayed.

icons/Mnk.png

This is a binary file and will not be displayed.

icons/Mrd.png

This is a binary file and will not be displayed.

icons/Nin.png

This is a binary file and will not be displayed.

icons/PET.png

This is a binary file and will not be displayed.

icons/Pgl.png

This is a binary file and will not be displayed.

icons/Pld.png

This is a binary file and will not be displayed.

icons/Rdm.png

This is a binary file and will not be displayed.

icons/Rog.png

This is a binary file and will not be displayed.

icons/Sam.png

This is a binary file and will not be displayed.

icons/Sch.png

This is a binary file and will not be displayed.

icons/Selene.png

This is a binary file and will not be displayed.

icons/Smn.png

This is a binary file and will not be displayed.

icons/Thm.png

This is a binary file and will not be displayed.

icons/Thumbs.db

This is a binary file and will not be displayed.

icons/Titan.png

This is a binary file and will not be displayed.

icons/WVR.png

This is a binary file and will not be displayed.

icons/War.png

This is a binary file and will not be displayed.

icons/Whm.png

This is a binary file and will not be displayed.

icons/choco.png

This is a binary file and will not be displayed.

icons/error.png

This is a binary file and will not be displayed.

+487
spelltimer.html
··· 1 + <html> 2 + <head> 3 + <meta charset="utf-8" /> 4 + <title></title> 5 + <style> 6 + * { 7 + /* フォント (一部のフォントは上手く適用されない) */ 8 + font-family: "Meiryo"; 9 + font-size: 12px; 10 + } 11 + 12 + body, html { 13 + margin: 0; 14 + } 15 + 16 + body { 17 + margin: 0.2em; 18 + } 19 + 20 + html { 21 + height: 100%; 22 + 23 + /* 外枠 */ 24 + /*border: 1px solid rgba(0,0,0,0.1);*/ 25 + /* はみ出た内容はスクロールバーを表示させずに隠す 26 + * 今のところ、ブラウザへの入力はできないので表示させても無意味 */ 27 + overflow: hidden; 28 + /* 背景色 */ 29 + background-color: transparent; 30 + } 31 + 32 + .resizeHandle { 33 + /* リサイズ用のハンドル 34 + * リサイズができる場所はウィンドウ右下の16x16ピクセルの場所 35 + * この部分が完全に透明だとマウス入力が透過してしまってサイズを変更できなくなる */ 36 + background-image: url(handle.png); 37 + background-position: bottom right; 38 + background-repeat: no-repeat; 39 + box-sizing: border-box; 40 + } 41 + 42 + * { 43 + color: #DED7BE; 44 + text-shadow: -1px 0 2px #795516, 0 1px 2px #795516, 1px 0 2px #795516, 0 -1px 2px #795516; 45 + font-weight: 300; 46 + /*white-space: nowrap;*/ 47 + } 48 + 49 + #title { 50 + border-bottom: 1px solid #DED7BE; 51 + } 52 + 53 + /* バーを囲う要素の設定 */ 54 + .progress-outer { 55 + box-sizing: border-box; 56 + position: relative; 57 + 58 + /* 背景色 */ 59 + background-color: rgba(0, 0, 0, 0.2); 60 + /* 枠の色 */ 61 + border: 1px solid rgba(192, 192, 192, 0.75); 62 + /* 陰の色 */ 63 + box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.1); 64 + /* 角を丸くする */ 65 + border-radius: 3px; 66 + /* バーの高さ(指定しないとだめ) */ 67 + height: 2em; 68 + /* タイトルとの間隔 */ 69 + margin-top: 1px; 70 + } 71 + 72 + .progress-outer:first-child { 73 + /* タイトルとの間隔 */ 74 + margin-top: 1px; 75 + } 76 + 77 + /* バーの設定 */ 78 + .progress-bar { 79 + opacity: 0.75; 80 + background-color: purple; 81 + position: absolute; 82 + height: 100%; 83 + border-right: 1px solid rgba(255, 255, 255, 0.7); 84 + box-sizing: border-box; 85 + border-radius: 3px; 86 + } 87 + 88 + /* バーに表示されるテキストの設定 */ 89 + .progress-text { 90 + position: absolute; 91 + width: 100%; 92 + display: table; 93 + table-layout: fixed; 94 + height: 100%; 95 + } 96 + 97 + /* タイマー名のテキスト */ 98 + .label-left { 99 + display: table-cell; 100 + text-align: left; 101 + vertical-align: middle; 102 + padding-left: 0.5em; 103 + white-space: nowrap; 104 + } 105 + 106 + /* 残り秒数のテキスト */ 107 + .label-right { 108 + display: table-cell; 109 + text-align: right; 110 + vertical-align: middle; 111 + padding-right: 0.5em; 112 + 113 + color: #E2EBF5; 114 + text-shadow: -1px 0 3px #217AA2, 0 1px 3px #217AA2, 1px 0 3px #217AA2, 0 -1px 3px #217AA2; 115 + font-weight: 300; 116 + white-space: nowrap; 117 + } 118 + 119 + </style> 120 + <script src="jquery-2.1.1.min.js" type="text/javascript"></script> 121 + <script> 122 + 123 + // 124 + // プラグイン側から以下のような ActXiv オブジェクトとしてデータが提供される 125 + // 126 + // var ActXiv = { 127 + // "timerFrames": [ 128 + // { 129 + // "name": "スペルタイマー名", 130 + // "key": "グループ名|スペルタイマー名", 131 + // "color": 色(0xAARRGGBB), 132 + // "startCount": タイマーの開始カウント, 133 + // "warningCount": タイマーの警告カウント, 134 + // "expireCount": タイマーの除去カウント, 135 + // "tooltip", "ツールチップ文字列", 136 + // "spellTimers": [ 137 + // { 138 + // "startTime": タイマーがカウントを開始した時刻(1970年1月1日からの経過秒数) 139 + // }, 140 + // ... 141 + // ] 142 + // }, 143 + // ... 144 + // ] 145 + // }; 146 + // 147 + // データの更新は 1 秒毎 148 + // プラグインからの更新の通知はないので、ひたすらこちら側で更新し続ける 149 + // 150 + 151 + /* 152 + // テスト用データ 153 + var ActXiv = { 154 + timerFrames: [ 155 + { 156 + "color": -16776961, // 0xFF0000FF 157 + "expireCount": -5, 158 + "key": " general|Timer1", 159 + "name": "Timer1", 160 + "startCount": 30, 161 + "tooltip": "", 162 + "warningCount": 10, 163 + "spellTimers": [ 164 + { "startTime": createTimestamp(-10) }, 165 + { "startTime": createTimestamp(-5) }, 166 + { "startTime": createTimestamp() }, 167 + ] 168 + }, 169 + { 170 + "color": -65536, // 0xFFFF0000 171 + "expireCount": -5, 172 + "key": " general|Timer2", 173 + "name": "Timer2", 174 + "startCount": 20, 175 + "tooltip": "", 176 + "warningCount": 10, 177 + "spellTimers": [ 178 + { "startTime": createTimestamp(-10) }, 179 + { "startTime": createTimestamp(-5) }, 180 + { "startTime": createTimestamp() }, 181 + ] 182 + }, 183 + ] 184 + }; 185 + function createTimestamp(dSecs) { 186 + var jsDate = new Date(); 187 + var offsetMinutes = new Date().getTimezoneOffset(); 188 + var jsTime = jsDate.getTime() - offsetMinutes * 60 * 1000; 189 + 190 + var delta = 0; 191 + if (typeof dSecs == "number") { 192 + delta = dSecs * 1000; 193 + } 194 + 195 + return jsTime + delta; 196 + } 197 + */ 198 + 199 + // onOverlayStateUpdate イベントを購読 200 + document.addEventListener("onOverlayStateUpdate", function (e) { 201 + if (!e.detail.isLocked) { 202 + displayResizeHandle(); 203 + } else { 204 + hideResizeHandle(); 205 + } 206 + }); 207 + 208 + function displayResizeHandle() { 209 + document.documentElement.classList.add("resizeHandle"); 210 + } 211 + 212 + function hideResizeHandle() { 213 + document.documentElement.classList.remove("resizeHandle"); 214 + } 215 + 216 + 217 + function initialize() { 218 + update(); 219 + setInterval("update()", 500); 220 + } 221 + 222 + // ★表示の流れ 223 + // 224 + // ActXiv オブジェクトに新しいタイマーがないかチェック 225 + // → timerFrame.onlyMasterTicks && !timerFrame.absoluteTiming なら、最新のタイマーで上書き 226 + // → あったらバー作成&アニメーション開始(アニメーション時間はバーの残り時間) 227 + // → 作成したバーを残り時間に応じた場所に挿入 228 + // → 作成済みのバーの中に期限切れのものがないかをチェック 229 + // → あったら除去 230 + // → 最初に戻る 231 + 232 + var bars = []; 233 + 234 + // 表示要素の更新 235 + function update() { 236 + if (typeof(ActXiv) == 'undefined') { return; } 237 + 238 + var containerDiv = document.getElementById('spelltimer'); 239 + if (typeof(ActXiv.timerFrames) != 'undefined') { 240 + ActXiv.timerFrames.forEach(function (timerFrame) { 241 + // 1 つのみ許容&最新のタイマーで上書きされる設定の場合 242 + if (timerFrame.spellTimers.length > 0 && 243 + timerFrame.onlyMasterTicks && 244 + !timerFrame.absoluteTiming) { 245 + // 最新のタイマーを捜す 246 + var latestSpellTimer; 247 + timerFrame.spellTimers.forEach(function (spellTimer) { 248 + if (typeof(latestSpellTimer) == 'undefined') { // 初回 249 + latestSpellTimer = spellTimer; 250 + } else if (latestSpellTimer.startTime < spellTimer.startTime) { // 2 回目以降 251 + latestSpellTimer = spellTimer; 252 + } 253 + }); 254 + // 最新のタイマーのみを処理にかける 255 + processActXivTimerEntry(containerDiv, timerFrame, latestSpellTimer); 256 + } 257 + else { // そうでない場合はすべてのタイマーを処理にかける 258 + timerFrame.spellTimers.forEach(function (spellTimer) { 259 + processActXivTimerEntry(containerDiv, timerFrame, spellTimer); 260 + }); 261 + } 262 + }); 263 + } 264 + 265 + // バーの更新と期限切れバーの削除 266 + var newBars = []; 267 + for (var i = 0; i < bars.length; i++) { 268 + if (!bars[i].spellTimer.getIsExpired()) { // 期限切れではない 269 + bars[i].updateBar(); // バー更新 270 + newBars.push(bars[i]); 271 + } else { 272 + bars[i].removeBarElement(); // バーを親要素から削除 273 + } 274 + } 275 + bars = newBars; 276 + 277 + ActXiv = {}; 278 + } 279 + 280 + function processActXivTimerEntry(container, _tf, _st) { 281 + var spellTimer = new SpellTimer(_tf, _st); 282 + 283 + if (spellTimer.getIsExpired()) { 284 + return; // 渡されたデータが既に期限切れだった場合は何もせずに次へ 285 + }; 286 + 287 + var bar = getTimerBarFromList(spellTimer); 288 + 289 + // リセットが必要な場合、既に存在するバーを消して作り直す 290 + if (typeof (bar) == 'undefined' && _tf.onlyMasterTicks && !_tf.absoluteTiming) { 291 + for (var i = bars.length - 1; i >= 0; i--) { 292 + if (bars[i].spellTimer.key == _tf.key) { 293 + bars[i].removeBarElement(); 294 + bars.splice(i, 1); 295 + } 296 + } 297 + } 298 + 299 + if (typeof(bar) == 'undefined') { 300 + bar = new TimerBar(spellTimer); 301 + 302 + // テキストの表示方法を設定 303 + bar.setBarLabel(function (_st) { 304 + var nameText = _st.name; 305 + var remaining = _st.getRemaining(); 306 + var timerText; 307 + if (remaining <= 0) { 308 + timerText = "READY"; // カウントが0以下なら READY と表示 309 + } else { 310 + timerText = _st.getRemaining().toFixed(0) + "s"; // それ以上ならカウントを表示 311 + } 312 + 313 + return "<span class='label-left'><span class='timer-name'>" + nameText + "</span>:</span>" + 314 + "<span class='label-right timer-remaining'>" + timerText + "</span>"; 315 + }); 316 + bar.useHTMLLabel = true; // HTML で表示する 317 + // 色設定 318 + bar.setBarColor(getColorCodeFromNumber(bar.spellTimer.color)); 319 + // バーの高さ設定 320 + //bar.setBarHeight(20); 321 + 322 + // 残り時間を考慮してリストに追加 323 + var i; 324 + var inserted = false; 325 + for (i = 0; i < bars.length; i++) { 326 + if (spellTimer.getRemaining() < bars[i].spellTimer.getRemaining()) { 327 + bars.splice(i, 0, bar); 328 + inserted = true; 329 + break; 330 + } 331 + } 332 + if (!inserted) { 333 + bars.push(bar); 334 + } 335 + 336 + // 並び順を考慮してコンテナに追加 337 + var nextElement; 338 + if (bars[i + 1]) { 339 + nextElement = bars[i + 1].barElement; 340 + } 341 + container.insertBefore(bar.barElement, nextElement) 342 + } 343 + } 344 + 345 + function getTimerBarFromList(spellTimer) { 346 + var uniqueName = TimerBar.createUniqueName(spellTimer); 347 + for (var i = 0; i < bars.length; i++) { 348 + if (bars[i].uniqueName == uniqueName) { 349 + return bars[i]; 350 + } 351 + } 352 + return; 353 + } 354 + 355 + // SpellTimer オブジェクト 356 + var SpellTimer = (function (tf, st) { 357 + this.color = tf.color; 358 + this.expireCount = tf.expireCount; 359 + this.key = tf.key; 360 + this.name = tf.name; 361 + this.startCount = tf.startCount; 362 + this.tooltip = tf.tooltip; 363 + this.warningCount = tf.warningCount; 364 + this.startTime = st.startTime; 365 + }); 366 + SpellTimer.prototype = { 367 + getIsExpired: function () { 368 + if (this.getRemaining() < this.expireCount) { 369 + return true; 370 + } else { 371 + return false; 372 + } 373 + }, 374 + getElapsed: function() { 375 + return getElapsedSeconds(this.startTime); 376 + }, 377 + getRemaining: function () { 378 + return this.startCount - this.getElapsed(); 379 + } 380 + }; 381 + 382 + // TimerBar オブジェクト 383 + var TimerBar = (function (_spellTimer) { 384 + 385 + this.spellTimer = _spellTimer; 386 + 387 + this.uniqueName = TimerBar.createUniqueName(this.spellTimer); 388 + this.barElement = this._createProgressBarElement(); 389 + this.labelFunc = (function (_bar) { return ""; }); 390 + this.useHTMLLabel = false; 391 + 392 + }); 393 + TimerBar.createUniqueName = function (spellTimer) { 394 + return "TBAR_" + spellTimer.name + "_" + spellTimer.startTime.toString(); 395 + } 396 + 397 + TimerBar.prototype = { 398 + setBarLabel: function (strOrFunc) { 399 + if (typeof (strOrFunc) == "function") { 400 + this.labelFunc = strOrFunc; 401 + } else { 402 + this.labelFunc = function () { return strOrFunc; }; 403 + } 404 + }, 405 + setBarColor: function (color) { 406 + this.barElement.children[0].style.backgroundColor = color; 407 + }, 408 + setBarHeight: function (height) { 409 + this.barElement.style.height = height; 410 + }, 411 + updateBar: function () { 412 + var text = this.labelFunc(this.spellTimer); 413 + if (this.useHTMLLabel) { 414 + this.barElement.children[1].innerHTML = text; 415 + } else { 416 + this.barElement.children[1].innerText = text; 417 + } 418 + }, 419 + _createProgressBarElement: function () { 420 + var outerDiv = document.createElement("div"); 421 + outerDiv.className = "progress-outer"; 422 + var barDiv = document.createElement("div"); 423 + barDiv.className = "progress-bar"; 424 + var textDiv = document.createElement("div"); 425 + textDiv.className = "progress-text"; 426 + 427 + outerDiv.appendChild(barDiv); 428 + outerDiv.appendChild(textDiv); 429 + 430 + var percentage = Math.max(0, Math.min(1, this.spellTimer.getRemaining() / this.spellTimer.startCount)) * 100; 431 + barDiv.style.width = percentage.toFixed(2) + "%"; 432 + 433 + setTimeout(function(barDiv) { 434 + barDiv.style.backgroundColor = "#FF0000"; 435 + }, Math.max(0, (this.spellTimer.getRemaining() - this.spellTimer.warningCount) * 1000), barDiv); 436 + 437 + $(barDiv).animate( 438 + { width: "0%" }, 439 + Math.max(0, this.spellTimer.getRemaining() * 1000), 440 + "linear"); 441 + 442 + return outerDiv; 443 + }, 444 + removeBarElement: function () { 445 + this.barElement.parentElement.removeChild(this.barElement); 446 + } 447 + }; 448 + 449 + 450 + function getElapsedSeconds(startTime) { 451 + var jsDate = new Date(); 452 + var offsetMinutes = new Date().getTimezoneOffset(); 453 + var jsTime = jsDate.getTime() - offsetMinutes * 60 * 1000; 454 + 455 + return (jsTime - startTime) / 1000; 456 + } 457 + 458 + function formatTimeSpan(seconds) { 459 + 460 + } 461 + 462 + function getColorCodeFromNumber(number) { 463 + // 各要素を取得 464 + var alpha = ((number >> 24) & 0xFF) / 255.0; 465 + var red = (number >> 16) & 0xFF; 466 + var green = (number >> 8) & 0xFF; 467 + var blue = number & 0xFF; 468 + 469 + // rgba(red, green, blue, alpha) 470 + return "rgba(" + red + ", " + green + ", " + blue + ", " + alpha + ")"; 471 + } 472 + 473 + 474 + </script> 475 + </head> 476 + <body onload="initialize();"> 477 + 478 + <div id="title"> 479 + Timer 480 + </div> 481 + 482 + <div id="spelltimer"> 483 + 484 + </div> 485 + 486 + </body> 487 + </html>
wav/chime13.wav

This is a binary file and will not be displayed.

wav/onepoint14.wav

This is a binary file and will not be displayed.

wav/onepoint23.wav

This is a binary file and will not be displayed.

wav/onepoint24.wav

This is a binary file and will not be displayed.

wav/onepoint26.wav

This is a binary file and will not be displayed.

wav/onepoint30.wav

This is a binary file and will not be displayed.

wav/onepoint32.wav

This is a binary file and will not be displayed.

wav/onepoint33.wav

This is a binary file and will not be displayed.

wav/system46.wav

This is a binary file and will not be displayed.

wav/system47.wav

This is a binary file and will not be displayed.